Merge branch 'release-candidate' into release
							
								
								
									
										77
									
								
								.github/workflows/build.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						| @ -32,6 +32,7 @@ jobs: | ||||
|         with: | ||||
|           fetch-depth: 0 | ||||
|           submodules: true | ||||
|           ref: ${{ github.event.pull_request.head.sha }} | ||||
| 
 | ||||
|       - name: 'Docker cache' | ||||
|         uses: satackey/action-docker-layer-caching@v0.0.11 | ||||
| @ -48,13 +49,27 @@ jobs: | ||||
|           test -d artifacts && rm -rf artifacts || true | ||||
|           mkdir artifacts | ||||
|        | ||||
|       - name: 'Generate tag suffix' | ||||
|         if: startsWith(github.ref, 'refs/tags/') == true | ||||
|         run: echo "SUFFIX=$(git describe --tags --abbrev=0)"  >> $GITHUB_ENV | ||||
|       - name: 'Generate suffix and folder name' | ||||
|         id: names | ||||
|         run: | | ||||
|           REF=${{ github.ref }} | ||||
|           if [[ ${{ github.event_name }} == 'pull_request' ]]; then | ||||
|             REF=${{ github.head_ref }} | ||||
|           fi | ||||
|           BRANCH_OR_TAG=${REF##*/} | ||||
|           SHA=$(git rev-parse --short HEAD) | ||||
|            | ||||
|       - name: 'Generate branch suffix' | ||||
|         if: startsWith(github.ref, 'refs/tags/') != true | ||||
|         run: echo "SUFFIX=$(git rev-parse --abbrev-ref HEAD)-$(date +'%d%m%Y')-$(git rev-parse --short HEAD)" >> $GITHUB_ENV | ||||
|           if [[ "${{ github.ref }}" == "refs/tags/"* ]]; then | ||||
|             SUFFIX=${BRANCH_OR_TAG} | ||||
|           else | ||||
|             SUFFIX=${BRANCH_OR_TAG}-$(date +'%d%m%Y')-${SHA} | ||||
|           fi | ||||
|            | ||||
|           echo "WORKFLOW_BRANCH_OR_TAG=${BRANCH_OR_TAG}" >> $GITHUB_ENV | ||||
|           echo "::set-output name=artifacts-path::${BRANCH_OR_TAG}" | ||||
|           echo "::set-output name=suffix::${SUFFIX}" | ||||
|           echo "::set-output name=short-hash::${SHA}" | ||||
|           echo "::set-output name=latest-target::${TARGETS[${#TARGETS[@]}-1]}" | ||||
| 
 | ||||
|       - name: 'Build bootloader in docker' | ||||
|         uses: ./.github/actions/docker | ||||
| @ -95,17 +110,17 @@ jobs: | ||||
|             for TARGET in ${TARGETS} | ||||
|             do | ||||
|               mv bootloader/.obj/${TARGET}/bootloader.dfu \ | ||||
|                 artifacts/flipper-z-${TARGET}-bootloader-${SUFFIX}.dfu | ||||
|                 artifacts/flipper-z-${TARGET}-bootloader-${{steps.names.outputs.suffix}}.dfu | ||||
|               mv bootloader/.obj/${TARGET}/bootloader.bin \ | ||||
|                 artifacts/flipper-z-${TARGET}-bootloader-${SUFFIX}.bin | ||||
|                 artifacts/flipper-z-${TARGET}-bootloader-${{steps.names.outputs.suffix}}.bin | ||||
|               mv bootloader/.obj/${TARGET}/bootloader.elf \ | ||||
|                 artifacts/flipper-z-${TARGET}-bootloader-${SUFFIX}.elf | ||||
|                 artifacts/flipper-z-${TARGET}-bootloader-${{steps.names.outputs.suffix}}.elf | ||||
|               mv firmware/.obj/${TARGET}/firmware.dfu \ | ||||
|                 artifacts/flipper-z-${TARGET}-firmware-${SUFFIX}.dfu | ||||
|                 artifacts/flipper-z-${TARGET}-firmware-${{steps.names.outputs.suffix}}.dfu | ||||
|               mv firmware/.obj/${TARGET}/firmware.bin \ | ||||
|                 artifacts/flipper-z-${TARGET}-firmware-${SUFFIX}.bin | ||||
|                 artifacts/flipper-z-${TARGET}-firmware-${{steps.names.outputs.suffix}}.bin | ||||
|               mv firmware/.obj/${TARGET}/firmware.elf \ | ||||
|                 artifacts/flipper-z-${TARGET}-firmware-${SUFFIX}.elf | ||||
|                 artifacts/flipper-z-${TARGET}-firmware-${{steps.names.outputs.suffix}}.elf | ||||
|             done | ||||
| 
 | ||||
|       - name: 'Generate full dfu file' | ||||
| @ -117,7 +132,7 @@ jobs: | ||||
|             do | ||||
|               hex2dfu \ | ||||
|                 -i firmware/.obj/${TARGET}/full.hex \ | ||||
|                 -o artifacts/flipper-z-${TARGET}-full-${SUFFIX}.dfu \ | ||||
|                 -o artifacts/flipper-z-${TARGET}-full-${{steps.names.outputs.suffix}}.dfu \ | ||||
|                 -l "Flipper Zero $(echo $TARGET | tr a-z A-Z)" | ||||
|             done | ||||
| 
 | ||||
| @ -127,8 +142,8 @@ jobs: | ||||
|           for TARGET in ${TARGETS} | ||||
|           do | ||||
|             cp \ | ||||
|               artifacts/flipper-z-${TARGET}-bootloader-${SUFFIX}.bin \ | ||||
|               artifacts/flipper-z-${TARGET}-full-${SUFFIX}.bin | ||||
|               artifacts/flipper-z-${TARGET}-bootloader-${{steps.names.outputs.suffix}}.bin \ | ||||
|               artifacts/flipper-z-${TARGET}-full-${{steps.names.outputs.suffix}}.bin | ||||
|           done | ||||
| 
 | ||||
|       - name: 'Full flash asssembly: bootloader padding' | ||||
| @ -136,7 +151,7 @@ jobs: | ||||
|         run: | | ||||
|           for TARGET in ${TARGETS} | ||||
|           do | ||||
|             truncate -s 32768 artifacts/flipper-z-${TARGET}-full-${SUFFIX}.bin | ||||
|             truncate -s 32768 artifacts/flipper-z-${TARGET}-full-${{steps.names.outputs.suffix}}.bin | ||||
|           done | ||||
| 
 | ||||
|       - name: 'Full flash asssembly: append firmware' | ||||
| @ -145,8 +160,8 @@ jobs: | ||||
|           for TARGET in ${TARGETS} | ||||
|           do | ||||
|             cat \ | ||||
|               artifacts/flipper-z-${TARGET}-firmware-${SUFFIX}.bin \ | ||||
|               >> artifacts/flipper-z-${TARGET}-full-${SUFFIX}.bin | ||||
|               artifacts/flipper-z-${TARGET}-firmware-${{steps.names.outputs.suffix}}.bin \ | ||||
|               >> artifacts/flipper-z-${TARGET}-full-${{steps.names.outputs.suffix}}.bin | ||||
|           done | ||||
| 
 | ||||
|       - name: 'Bundle core2 firmware' | ||||
| @ -160,17 +175,17 @@ jobs: | ||||
|             lib/STM32CubeWB/Projects/STM32WB_Copro_Wireless_Binaries/STM32WB5x/stm32wb5x_FUS_fw_for_fus_0_5_3.bin \ | ||||
|             lib/STM32CubeWB/Projects/STM32WB_Copro_Wireless_Binaries/STM32WB5x/stm32wb5x_BLE_Stack_full_fw.bin \ | ||||
|             core2_firmware | ||||
|           tar czpf artifacts/flipper-z-any-core2_firmware-${SUFFIX}.tgz core2_firmware | ||||
|           tar czpf artifacts/flipper-z-any-core2_firmware-${{steps.names.outputs.suffix}}.tgz core2_firmware | ||||
| 
 | ||||
|       - name: 'Bundle scripts' | ||||
|         if: ${{ !github.event.pull_request.head.repo.fork }} | ||||
|         run: | | ||||
|           tar czpf artifacts/flipper-z-any-scripts-${SUFFIX}.tgz scripts | ||||
|           tar czpf artifacts/flipper-z-any-scripts-${{steps.names.outputs.suffix}}.tgz scripts | ||||
| 
 | ||||
|       - name: 'Bundle resources' | ||||
|         if: ${{ !github.event.pull_request.head.repo.fork }} | ||||
|         run: | | ||||
|           tar czpf artifacts/flipper-z-any-resources-${SUFFIX}.tgz -C assets resources | ||||
|           tar czpf artifacts/flipper-z-any-resources-${{steps.names.outputs.suffix}}.tgz -C assets resources | ||||
| 
 | ||||
|       - name: 'Upload artifacts to update server' | ||||
|         if: ${{ !github.event.pull_request.head.repo.fork }} | ||||
| @ -178,7 +193,7 @@ jobs: | ||||
|         with: | ||||
|           switches: -avzP --delete | ||||
|           path: artifacts/ | ||||
|           remote_path: "${{ secrets.RSYNC_DEPLOY_BASE_PATH }}${GITHUB_REF##*/}/" | ||||
|           remote_path: "${{ secrets.RSYNC_DEPLOY_BASE_PATH }}${{steps.names.outputs.artifacts-path}}/" | ||||
|           remote_host: ${{ secrets.RSYNC_DEPLOY_HOST }} | ||||
|           remote_port: ${{ secrets.RSYNC_DEPLOY_PORT }} | ||||
|           remote_user: ${{ secrets.RSYNC_DEPLOY_USER }} | ||||
| @ -190,3 +205,21 @@ jobs: | ||||
|         with: | ||||
|           args: -X POST -F 'key=${{ secrets.REINDEX_KEY }}' ${{ secrets.REINDEX_URL }} | ||||
| 
 | ||||
|       - name: Find Previous Comment | ||||
|         if: ${{ !github.event.pull_request.head.repo.fork && github.event.pull_request }} | ||||
|         uses: peter-evans/find-comment@v1 | ||||
|         id: fc | ||||
|         with: | ||||
|           issue-number: ${{ github.event.pull_request.number }} | ||||
|           comment-author: 'github-actions[bot]' | ||||
|           body-includes: 'to flash the' | ||||
| 
 | ||||
|       - name: Create or update comment | ||||
|         if: ${{ !github.event.pull_request.head.repo.fork && github.event.pull_request}} | ||||
|         uses: peter-evans/create-or-update-comment@v1 | ||||
|         with: | ||||
|           comment-id: ${{ steps.fc.outputs.comment-id }} | ||||
|           issue-number: ${{ github.event.pull_request.number }} | ||||
|           body: | | ||||
|             [Click here](https://update.flipperzero.one/?url=https://update.flipperzero.one/builds/firmware/${{steps.names.outputs.artifacts-path}}/flipper-z-${{steps.names.outputs.latest-target}}-full-${{steps.names.outputs.suffix}}.dfu&channel=${{steps.names.outputs.artifacts-path}}&version=${{steps.names.outputs.short-hash}}) to flash the `${{steps.names.outputs.short-hash}}` version of this branch via WebUSB. | ||||
|           edit-mode: replace | ||||
|  | ||||
							
								
								
									
										6
									
								
								.github/workflows/lint_c.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						| @ -43,19 +43,19 @@ jobs: | ||||
|       - name: 'Build docker image' | ||||
|         uses: ./.github/actions/docker | ||||
| 
 | ||||
|       - name: 'Check syntax' | ||||
|       - name: 'Check code formatting' | ||||
|         id: syntax_check | ||||
|         uses: ./.github/actions/docker | ||||
|         with: | ||||
|           run: SET_GH_OUTPUT=1 /syntax_check.sh | ||||
| 
 | ||||
|       - name: Report syntax errors | ||||
|       - name: Report code formatting errors | ||||
|         if: failure() && steps.syntax_check.outputs.errors && github.event.pull_request | ||||
|         uses: peter-evans/create-or-update-comment@v1 | ||||
|         with: | ||||
|           issue-number: ${{ github.event.pull_request.number }} | ||||
|           body: | | ||||
|             Please fix following syntax errors: | ||||
|             Please fix following code formatting errors: | ||||
|             ``` | ||||
|             ${{ steps.syntax_check.outputs.errors }} | ||||
|             ``` | ||||
|  | ||||
| @ -35,7 +35,6 @@ AccessorApp::AccessorApp() | ||||
|     : onewire_master{&ibutton_gpio} { | ||||
|     furi_hal_power_insomnia_enter(); | ||||
|     notification = static_cast<NotificationApp*>(furi_record_open("notification")); | ||||
|     notify_init(); | ||||
|     furi_hal_power_enable_otg(); | ||||
| } | ||||
| 
 | ||||
| @ -104,17 +103,6 @@ AccessorApp::Scene AccessorApp::get_previous_scene() { | ||||
| 
 | ||||
| /***************************** NOTIFY *******************************/ | ||||
| 
 | ||||
| void AccessorApp::notify_init() { | ||||
|     GPIO_InitTypeDef GPIO_InitStruct = {0}; | ||||
| 
 | ||||
|     GPIO_InitStruct.Pin = PB3_Pin; | ||||
|     GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; | ||||
|     GPIO_InitStruct.Pull = GPIO_NOPULL; | ||||
|     GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH; | ||||
|     GPIO_InitStruct.Alternate = GPIO_AF1_TIM2; | ||||
|     HAL_GPIO_Init(PB3_GPIO_Port, &GPIO_InitStruct); | ||||
| } | ||||
| 
 | ||||
| void AccessorApp::notify_green_blink() { | ||||
|     notification_message(notification, &sequence_blink_green_10); | ||||
| } | ||||
|  | ||||
| @ -29,9 +29,7 @@ public: | ||||
|     bool switch_to_previous_scene(uint8_t count = 1); | ||||
|     Scene get_previous_scene(); | ||||
| 
 | ||||
|     void notify_init(); | ||||
|     void notify_green_blink(); | ||||
| 
 | ||||
|     void notify_success(); | ||||
| 
 | ||||
|     char* get_text_store(); | ||||
|  | ||||
| @ -148,7 +148,7 @@ const FlipperApplication FLIPPER_APPS[] = { | ||||
| #endif | ||||
| 
 | ||||
| #ifdef APP_SUBGHZ | ||||
|     {.app = subghz_app, .name = "Sub-1 GHz", .stack_size = 2048, .icon = &A_Sub1ghz_14}, | ||||
|     {.app = subghz_app, .name = "Sub-GHz", .stack_size = 2048, .icon = &A_Sub1ghz_14}, | ||||
| #endif | ||||
| 
 | ||||
| #ifdef APP_LF_RFID | ||||
|  | ||||
| @ -2,20 +2,6 @@ | ||||
| 
 | ||||
| static bool archive_get_filenames(ArchiveApp* archive); | ||||
| 
 | ||||
| static bool is_favorite(ArchiveApp* archive, ArchiveFile_t* file) { | ||||
|     FileInfo file_info; | ||||
|     FS_Error fr; | ||||
|     string_t path; | ||||
| 
 | ||||
|     string_init_printf(path, "%s/%s", get_favorites_path(), string_get_cstr(file->name)); | ||||
| 
 | ||||
|     fr = storage_common_stat(archive->api, string_get_cstr(path), &file_info); | ||||
|     FURI_LOG_I("FAV", "%d", fr); | ||||
| 
 | ||||
|     string_clear(path); | ||||
|     return (fr == FSE_OK || fr == FSE_EXIST); | ||||
| } | ||||
| 
 | ||||
| static void update_offset(ArchiveApp* archive) { | ||||
|     furi_assert(archive); | ||||
| 
 | ||||
| @ -92,6 +78,7 @@ static void archive_leave_dir(ArchiveApp* archive) { | ||||
|         }); | ||||
| 
 | ||||
|     archive_switch_dir(archive, string_get_cstr(archive->browser.path)); | ||||
|     update_offset(archive); | ||||
| } | ||||
| 
 | ||||
| static void archive_enter_dir(ArchiveApp* archive, string_t name) { | ||||
| @ -144,26 +131,200 @@ static void set_file_type(ArchiveFile_t* file, FileInfo* file_info) { | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| static bool archive_get_filenames(ArchiveApp* archive) { | ||||
| static void archive_file_append(ArchiveApp* archive, const char* path, string_t string) { | ||||
|     furi_assert(archive); | ||||
|     furi_assert(path); | ||||
|     furi_assert(string); | ||||
| 
 | ||||
|     FileWorker* file_worker = file_worker_alloc(false); | ||||
| 
 | ||||
|     if(!file_worker_open(file_worker, path, FSAM_WRITE, FSOM_OPEN_APPEND)) { | ||||
|         FURI_LOG_E("Archive", "Append open error"); | ||||
|     } | ||||
| 
 | ||||
|     if(!file_worker_write(file_worker, string_get_cstr(string), string_size(string))) { | ||||
|         FURI_LOG_E("Archive", "Append write error"); | ||||
|     } | ||||
| 
 | ||||
|     file_worker_close(file_worker); | ||||
|     file_worker_free(file_worker); | ||||
| } | ||||
| 
 | ||||
| static void archive_view_add_item(ArchiveApp* archive, FileInfo* file_info, const char* name) { | ||||
|     furi_assert(archive); | ||||
|     furi_assert(file_info); | ||||
|     furi_assert(name); | ||||
| 
 | ||||
|     ArchiveFile_t item; | ||||
|     FileInfo file_info; | ||||
|     File* directory = storage_file_alloc(archive->api); | ||||
|     char name[MAX_NAME_LEN]; | ||||
| 
 | ||||
|     if(filter_by_extension(archive, file_info, name)) { | ||||
|         ArchiveFile_t_init(&item); | ||||
|         string_init_set_str(item.name, name); | ||||
|         set_file_type(&item, file_info); | ||||
| 
 | ||||
|         with_view_model( | ||||
|             archive->view_archive_main, (ArchiveViewModel * model) { | ||||
|             files_array_clean(model->files); | ||||
|                 files_array_push_back(model->files, item); | ||||
|                 return true; | ||||
|             }); | ||||
| 
 | ||||
|         ArchiveFile_t_clear(&item); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| static bool archive_is_favorite(ArchiveApp* archive, ArchiveFile_t* selected) { | ||||
|     furi_assert(selected); | ||||
|     string_t path; | ||||
|     string_t buffer; | ||||
|     string_init(buffer); | ||||
|     bool found = false; | ||||
| 
 | ||||
|     string_init_printf( | ||||
|         path, "%s/%s", string_get_cstr(archive->browser.path), string_get_cstr(selected->name)); | ||||
| 
 | ||||
|     bool load_result = | ||||
|         file_worker_open(archive->file_worker, ARCHIVE_FAV_PATH, FSAM_READ, FSOM_OPEN_ALWAYS); | ||||
| 
 | ||||
|     if(load_result) { | ||||
|         while(1) { | ||||
|             if(!file_worker_read_until(archive->file_worker, buffer, '\n')) { | ||||
|                 break; | ||||
|             } | ||||
|             if(!string_size(buffer)) { | ||||
|                 break; | ||||
|             } | ||||
|             if(!string_search(buffer, path)) { | ||||
|                 found = true; | ||||
|                 break; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     string_clear(buffer); | ||||
|     string_clear(path); | ||||
|     file_worker_close(archive->file_worker); | ||||
| 
 | ||||
|     return found; | ||||
| } | ||||
| 
 | ||||
| static bool archive_favorites_read(ArchiveApp* archive) { | ||||
|     string_t buffer; | ||||
|     FileInfo file_info; | ||||
|     string_init(buffer); | ||||
| 
 | ||||
|     bool load_result = | ||||
|         file_worker_open(archive->file_worker, ARCHIVE_FAV_PATH, FSAM_READ, FSOM_OPEN_EXISTING); | ||||
| 
 | ||||
|     if(load_result) { | ||||
|         while(1) { | ||||
|             if(!file_worker_read_until(archive->file_worker, buffer, '\n')) { | ||||
|                 break; | ||||
|             } | ||||
|             if(!string_size(buffer)) { | ||||
|                 break; | ||||
|             } | ||||
| 
 | ||||
|             archive_view_add_item(archive, &file_info, string_get_cstr(buffer)); | ||||
|             string_clean(buffer); | ||||
|         } | ||||
|     } | ||||
|     string_clear(buffer); | ||||
|     file_worker_close(archive->file_worker); | ||||
| 
 | ||||
|     return load_result; | ||||
| } | ||||
| 
 | ||||
| static bool | ||||
|     archive_favorites_rename(ArchiveApp* archive, ArchiveFile_t* selected, const char* dst) { | ||||
|     furi_assert(selected); | ||||
|     string_t path; | ||||
|     string_t buffer; | ||||
|     string_t temp; | ||||
| 
 | ||||
|     string_init(buffer); | ||||
|     string_init(temp); | ||||
| 
 | ||||
|     string_init_printf( | ||||
|         path, "%s/%s", string_get_cstr(archive->browser.path), string_get_cstr(selected->name)); | ||||
|     bool load_result = | ||||
|         file_worker_open(archive->file_worker, ARCHIVE_FAV_PATH, FSAM_READ, FSOM_OPEN_EXISTING); | ||||
| 
 | ||||
|     if(load_result) { | ||||
|         while(1) { | ||||
|             if(!file_worker_read_until(archive->file_worker, buffer, '\n')) { | ||||
|                 break; | ||||
|             } | ||||
|             if(!string_size(buffer)) { | ||||
|                 break; | ||||
|             } | ||||
| 
 | ||||
|             string_printf( | ||||
|                 temp, "%s\r\n", string_search(buffer, path) ? string_get_cstr(buffer) : dst); | ||||
|             archive_file_append(archive, ARCHIVE_FAV_TEMP_PATH, temp); | ||||
|             string_clean(temp); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     string_clear(temp); | ||||
|     string_clear(buffer); | ||||
|     string_clear(path); | ||||
| 
 | ||||
|     file_worker_close(archive->file_worker); | ||||
|     file_worker_remove(archive->file_worker, ARCHIVE_FAV_PATH); | ||||
|     file_worker_rename(archive->file_worker, ARCHIVE_FAV_TEMP_PATH, ARCHIVE_FAV_PATH); | ||||
| 
 | ||||
|     return load_result; | ||||
| } | ||||
| 
 | ||||
| static bool archive_favorites_delete(ArchiveApp* archive, ArchiveFile_t* selected) { | ||||
|     furi_assert(selected); | ||||
|     string_t path; | ||||
|     string_t buffer; | ||||
|     string_init(buffer); | ||||
| 
 | ||||
|     string_init_printf( | ||||
|         path, "%s/%s", string_get_cstr(archive->browser.path), string_get_cstr(selected->name)); | ||||
| 
 | ||||
|     bool load_result = | ||||
|         file_worker_open(archive->file_worker, ARCHIVE_FAV_PATH, FSAM_READ, FSOM_OPEN_EXISTING); | ||||
|     if(load_result) { | ||||
|         while(1) { | ||||
|             if(!file_worker_read_until(archive->file_worker, buffer, '\n')) { | ||||
|                 break; | ||||
|             } | ||||
|             if(!string_size(buffer)) { | ||||
|                 break; | ||||
|             } | ||||
| 
 | ||||
|             if(string_search(buffer, path)) { | ||||
|                 string_t temp; | ||||
|                 string_init_printf(temp, "%s\r\n", string_get_cstr(buffer)); | ||||
|                 archive_file_append(archive, ARCHIVE_FAV_TEMP_PATH, temp); | ||||
|                 string_clear(temp); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     string_clear(buffer); | ||||
|     string_clear(path); | ||||
| 
 | ||||
|     file_worker_close(archive->file_worker); | ||||
|     file_worker_remove(archive->file_worker, ARCHIVE_FAV_PATH); | ||||
|     file_worker_rename(archive->file_worker, ARCHIVE_FAV_TEMP_PATH, ARCHIVE_FAV_PATH); | ||||
| 
 | ||||
|     return load_result; | ||||
| } | ||||
| 
 | ||||
| static bool archive_read_dir(ArchiveApp* archive) { | ||||
|     FileInfo file_info; | ||||
|     File* directory = storage_file_alloc(archive->api); | ||||
|     char name[MAX_NAME_LEN]; | ||||
| 
 | ||||
|     if(!storage_dir_open(directory, string_get_cstr(archive->browser.path))) { | ||||
|         storage_dir_close(directory); | ||||
|         storage_file_free(directory); | ||||
|         return false; | ||||
|     } | ||||
| 
 | ||||
|     while(1) { | ||||
|         if(!storage_dir_read(directory, &file_info, name, MAX_NAME_LEN)) { | ||||
|             break; | ||||
| @ -180,31 +341,34 @@ static bool archive_get_filenames(ArchiveApp* archive) { | ||||
|         if(files_cnt > MAX_FILES) { | ||||
|             break; | ||||
|         } else if(storage_file_get_error(directory) == FSE_OK) { | ||||
|             if(filter_by_extension(archive, &file_info, name)) { | ||||
|                 ArchiveFile_t_init(&item); | ||||
|                 string_init_set(item.name, name); | ||||
|                 set_file_type(&item, &file_info); | ||||
| 
 | ||||
|                 with_view_model( | ||||
|                     archive->view_archive_main, (ArchiveViewModel * model) { | ||||
|                         files_array_push_back(model->files, item); | ||||
|                         return true; | ||||
|                     }); | ||||
| 
 | ||||
|                 ArchiveFile_t_clear(&item); | ||||
|             } | ||||
|             archive_view_add_item(archive, &file_info, name); | ||||
|         } else { | ||||
|             storage_dir_close(directory); | ||||
|             storage_file_free(directory); | ||||
|             return false; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     storage_dir_close(directory); | ||||
|     storage_file_free(directory); | ||||
| 
 | ||||
|     return true; | ||||
| } | ||||
| 
 | ||||
| static bool archive_get_filenames(ArchiveApp* archive) { | ||||
|     furi_assert(archive); | ||||
|     with_view_model( | ||||
|         archive->view_archive_main, (ArchiveViewModel * model) { | ||||
|             files_array_clean(model->files); | ||||
|             return true; | ||||
|         }); | ||||
| 
 | ||||
|     if(archive->browser.tab_id != ArchiveTabFavorites) { | ||||
|         archive_read_dir(archive); | ||||
|     } else { | ||||
|         archive_favorites_read(archive); | ||||
|     } | ||||
|     return true; | ||||
| } | ||||
| static void archive_exit_callback(ArchiveApp* archive) { | ||||
|     furi_assert(archive); | ||||
| 
 | ||||
| @ -220,24 +384,17 @@ static uint32_t archive_previous_callback(void* context) { | ||||
| /* file menu */ | ||||
| static void archive_add_to_favorites(ArchiveApp* archive) { | ||||
|     furi_assert(archive); | ||||
| 
 | ||||
|     storage_common_mkdir(archive->api, get_favorites_path()); | ||||
| 
 | ||||
|     string_t buffer_src; | ||||
|     string_t buffer_dst; | ||||
| 
 | ||||
|     string_init_printf( | ||||
|         buffer_src, | ||||
|         "%s/%s", | ||||
|         "%s/%s\r\n", | ||||
|         string_get_cstr(archive->browser.path), | ||||
|         string_get_cstr(archive->browser.name)); | ||||
|     string_init_printf( | ||||
|         buffer_dst, "%s/%s", get_favorites_path(), string_get_cstr(archive->browser.name)); | ||||
| 
 | ||||
|     storage_common_copy(archive->api, string_get_cstr(buffer_src), string_get_cstr(buffer_dst)); | ||||
|     archive_file_append(archive, ARCHIVE_FAV_PATH, buffer_src); | ||||
| 
 | ||||
|     string_clear(buffer_src); | ||||
|     string_clear(buffer_dst); | ||||
| } | ||||
| 
 | ||||
| static void archive_text_input_callback(void* context) { | ||||
| @ -259,6 +416,7 @@ static void archive_text_input_callback(void* context) { | ||||
|         string_get_cstr(archive->browser.path), | ||||
|         archive->browser.text_input_buffer); | ||||
| 
 | ||||
|     string_set(archive->browser.name, archive->browser.text_input_buffer); | ||||
|     // append extension
 | ||||
| 
 | ||||
|     ArchiveFile_t* file; | ||||
| @ -267,18 +425,38 @@ static void archive_text_input_callback(void* context) { | ||||
|         archive->view_archive_main, (ArchiveViewModel * model) { | ||||
|             file = files_array_get( | ||||
|                 model->files, CLAMP(model->idx, files_array_size(model->files) - 1, 0)); | ||||
|             file->fav = archive_is_favorite(archive, file); | ||||
| 
 | ||||
|             return true; | ||||
|         }); | ||||
| 
 | ||||
|     string_cat(buffer_dst, known_ext[file->type]); | ||||
|     storage_common_rename(archive->api, string_get_cstr(buffer_src), string_get_cstr(buffer_dst)); | ||||
| 
 | ||||
|     if(file->fav) { | ||||
|         archive_favorites_rename(archive, file, string_get_cstr(buffer_dst)); | ||||
|     } | ||||
| 
 | ||||
|     view_dispatcher_switch_to_view(archive->view_dispatcher, ArchiveViewMain); | ||||
|     archive_get_filenames(archive); | ||||
| 
 | ||||
|     with_view_model( | ||||
|         archive->view_archive_main, (ArchiveViewModel * model) { | ||||
|             model->idx = 0; | ||||
|             while(model->idx < files_array_size(model->files)) { | ||||
|                 ArchiveFile_t* current = files_array_get(model->files, model->idx); | ||||
|                 if(!string_search(current->name, archive->browser.text_input_buffer)) { | ||||
|                     break; | ||||
|                 } | ||||
|                 ++model->idx; | ||||
|             } | ||||
|             return true; | ||||
|         }); | ||||
| 
 | ||||
|     update_offset(archive); | ||||
| 
 | ||||
|     string_clear(buffer_src); | ||||
|     string_clear(buffer_dst); | ||||
| 
 | ||||
|     archive_get_filenames(archive); | ||||
| } | ||||
| 
 | ||||
| static void archive_enter_text_input(ArchiveApp* archive) { | ||||
| @ -314,7 +492,8 @@ static void archive_show_file_menu(ArchiveApp* archive) { | ||||
|             selected = files_array_get(model->files, model->idx); | ||||
|             model->menu = true; | ||||
|             model->menu_idx = 0; | ||||
|             selected->fav = is_favorite(archive, selected); | ||||
|             selected->fav = is_known_app(selected->type) ? archive_is_favorite(archive, selected) : | ||||
|                                                            false; | ||||
| 
 | ||||
|             return true; | ||||
|         }); | ||||
| @ -340,28 +519,21 @@ static void archive_open_app(ArchiveApp* archive, const char* app_name, const ch | ||||
|     loader_start(archive->loader, app_name, args); | ||||
| } | ||||
| 
 | ||||
| static void archive_delete_file(ArchiveApp* archive, ArchiveFile_t* file, bool fav, bool orig) { | ||||
| static void archive_delete_file(ArchiveApp* archive, ArchiveFile_t* file) { | ||||
|     furi_assert(archive); | ||||
|     furi_assert(file); | ||||
| 
 | ||||
|     string_t path; | ||||
|     string_init(path); | ||||
| 
 | ||||
|     if(!fav && !orig) { | ||||
|     string_printf( | ||||
|         path, "%s/%s", string_get_cstr(archive->browser.path), string_get_cstr(file->name)); | ||||
|         storage_common_remove(archive->api, string_get_cstr(path)); | ||||
| 
 | ||||
|     } else { // remove from favorites
 | ||||
|         string_printf(path, "%s/%s", get_favorites_path(), string_get_cstr(file->name)); | ||||
|         storage_common_remove(archive->api, string_get_cstr(path)); | ||||
|     if(archive_is_favorite(archive, file)) { // remove from favorites
 | ||||
|         archive_favorites_delete(archive, file); | ||||
|     } | ||||
| 
 | ||||
|         if(orig) { // remove original file
 | ||||
|             string_printf( | ||||
|                 path, "%s/%s", get_default_path(file->type), string_get_cstr(file->name)); | ||||
|             storage_common_remove(archive->api, string_get_cstr(path)); | ||||
|         } | ||||
|     } | ||||
|     file_worker_remove(archive->file_worker, string_get_cstr(path)); | ||||
| 
 | ||||
|     string_clear(path); | ||||
|     archive_get_filenames(archive); | ||||
| @ -375,6 +547,24 @@ static void archive_delete_file(ArchiveApp* archive, ArchiveFile_t* file, bool f | ||||
|     update_offset(archive); | ||||
| } | ||||
| 
 | ||||
| static void | ||||
|     archive_run_in_app(ArchiveApp* archive, ArchiveFile_t* selected, bool full_path_provided) { | ||||
|     string_t full_path; | ||||
| 
 | ||||
|     if(!full_path_provided) { | ||||
|         string_init_printf( | ||||
|             full_path, | ||||
|             "%s/%s", | ||||
|             string_get_cstr(archive->browser.path), | ||||
|             string_get_cstr(selected->name)); | ||||
|     } else { | ||||
|         string_init_set(full_path, selected->name); | ||||
|     } | ||||
| 
 | ||||
|     archive_open_app(archive, flipper_app_name[selected->type], string_get_cstr(full_path)); | ||||
|     string_clear(full_path); | ||||
| } | ||||
| 
 | ||||
| static void archive_file_menu_callback(ArchiveApp* archive) { | ||||
|     furi_assert(archive); | ||||
| 
 | ||||
| @ -391,27 +581,17 @@ static void archive_file_menu_callback(ArchiveApp* archive) { | ||||
|     switch(idx) { | ||||
|     case 0: | ||||
|         if(is_known_app(selected->type)) { | ||||
|             string_t full_path; | ||||
|             string_init_printf( | ||||
|                 full_path, | ||||
|                 "%s/%s", | ||||
|                 string_get_cstr(archive->browser.path), | ||||
|                 string_get_cstr(selected->name)); | ||||
| 
 | ||||
|             archive_open_app( | ||||
|                 archive, flipper_app_name[selected->type], string_get_cstr(full_path)); | ||||
| 
 | ||||
|             string_clear(full_path); | ||||
|             archive_run_in_app(archive, selected, false); | ||||
|         } | ||||
|         break; | ||||
|     case 1: | ||||
|         if(is_known_app(selected->type)) { | ||||
|             if(!is_favorite(archive, selected)) { | ||||
|             if(!archive_is_favorite(archive, selected)) { | ||||
|                 string_set(archive->browser.name, selected->name); | ||||
|                 archive_add_to_favorites(archive); | ||||
|             } else { | ||||
|                 // delete from favorites
 | ||||
|                 archive_delete_file(archive, selected, true, false); | ||||
|                 archive_favorites_delete(archive, selected); | ||||
|             } | ||||
|             archive_close_file_menu(archive); | ||||
|         } | ||||
| @ -424,13 +604,7 @@ static void archive_file_menu_callback(ArchiveApp* archive) { | ||||
|         break; | ||||
|     case 3: | ||||
|         // confirmation?
 | ||||
|         if(is_favorite(archive, selected)) { | ||||
|             //delete both fav & original
 | ||||
|             archive_delete_file(archive, selected, true, true); | ||||
|         } else { | ||||
|             archive_delete_file(archive, selected, false, false); | ||||
|         } | ||||
| 
 | ||||
|         archive_delete_file(archive, selected); | ||||
|         archive_close_file_menu(archive); | ||||
| 
 | ||||
|         break; | ||||
| @ -545,11 +719,17 @@ static bool archive_view_input(InputEvent* event, void* context) { | ||||
|                 } | ||||
|             } else { | ||||
|                 if(event->type == InputTypeShort) { | ||||
|                     if(archive->browser.tab_id == ArchiveTabFavorites) { | ||||
|                         if(is_known_app(selected->type)) { | ||||
|                             archive_run_in_app(archive, selected, true); | ||||
|                         } | ||||
|                     } else { | ||||
|                         archive_show_file_menu(archive); | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     update_offset(archive); | ||||
| 
 | ||||
| @ -559,6 +739,8 @@ static bool archive_view_input(InputEvent* event, void* context) { | ||||
| void archive_free(ArchiveApp* archive) { | ||||
|     furi_assert(archive); | ||||
| 
 | ||||
|     file_worker_free(archive->file_worker); | ||||
| 
 | ||||
|     view_dispatcher_remove_view(archive->view_dispatcher, ArchiveViewMain); | ||||
|     view_dispatcher_remove_view(archive->view_dispatcher, ArchiveViewTextInput); | ||||
|     view_dispatcher_free(archive->view_dispatcher); | ||||
| @ -598,6 +780,7 @@ ArchiveApp* archive_alloc() { | ||||
|     archive->api = furi_record_open("storage"); | ||||
|     archive->text_input = text_input_alloc(); | ||||
|     archive->view_archive_main = view_alloc(); | ||||
|     archive->file_worker = file_worker_alloc(true); | ||||
| 
 | ||||
|     furi_check(archive->event_queue); | ||||
| 
 | ||||
|  | ||||
| @ -13,10 +13,13 @@ | ||||
| #include <storage/storage.h> | ||||
| #include "archive_views.h" | ||||
| #include "applications.h" | ||||
| #include "file-worker.h" | ||||
| 
 | ||||
| #define MAX_DEPTH 32 | ||||
| #define MAX_FILES 100 //temp
 | ||||
| #define MAX_FILE_SIZE 128 | ||||
| #define ARCHIVE_FAV_PATH "/any/favorites.txt" | ||||
| #define ARCHIVE_FAV_TEMP_PATH "/any/favorites.tmp" | ||||
| 
 | ||||
| typedef enum { | ||||
|     ArchiveViewMain, | ||||
| @ -27,7 +30,7 @@ typedef enum { | ||||
| static const char* flipper_app_name[] = { | ||||
|     [ArchiveFileTypeIButton] = "iButton", | ||||
|     [ArchiveFileTypeNFC] = "NFC", | ||||
|     [ArchiveFileTypeSubOne] = "Sub-1 GHz", | ||||
|     [ArchiveFileTypeSubGhz] = "Sub-GHz", | ||||
|     [ArchiveFileTypeLFRFID] = "125 kHz RFID", | ||||
|     [ArchiveFileTypeIrda] = "Infrared", | ||||
| }; | ||||
| @ -35,7 +38,7 @@ static const char* flipper_app_name[] = { | ||||
| static const char* known_ext[] = { | ||||
|     [ArchiveFileTypeIButton] = ".ibtn", | ||||
|     [ArchiveFileTypeNFC] = ".nfc", | ||||
|     [ArchiveFileTypeSubOne] = ".sub", | ||||
|     [ArchiveFileTypeSubGhz] = ".sub", | ||||
|     [ArchiveFileTypeLFRFID] = ".rfid", | ||||
|     [ArchiveFileTypeIrda] = ".ir", | ||||
| }; | ||||
| @ -44,7 +47,7 @@ static const char* tab_default_paths[] = { | ||||
|     [ArchiveTabFavorites] = "/any/favorites", | ||||
|     [ArchiveTabIButton] = "/any/ibutton", | ||||
|     [ArchiveTabNFC] = "/any/nfc", | ||||
|     [ArchiveTabSubOne] = "/any/subghz/saved", | ||||
|     [ArchiveTabSubGhz] = "/any/subghz/saved", | ||||
|     [ArchiveTabLFRFID] = "/any/lfrfid", | ||||
|     [ArchiveTabIrda] = "/any/irda", | ||||
|     [ArchiveTabBrowser] = "/any", | ||||
| @ -56,8 +59,8 @@ static inline const char* get_tab_ext(ArchiveTabEnum tab) { | ||||
|         return known_ext[ArchiveFileTypeIButton]; | ||||
|     case ArchiveTabNFC: | ||||
|         return known_ext[ArchiveFileTypeNFC]; | ||||
|     case ArchiveTabSubOne: | ||||
|         return known_ext[ArchiveFileTypeSubOne]; | ||||
|     case ArchiveTabSubGhz: | ||||
|         return known_ext[ArchiveFileTypeSubGhz]; | ||||
|     case ArchiveTabLFRFID: | ||||
|         return known_ext[ArchiveFileTypeLFRFID]; | ||||
|     case ArchiveTabIrda: | ||||
| @ -73,8 +76,8 @@ static inline const char* get_default_path(ArchiveFileTypeEnum type) { | ||||
|         return tab_default_paths[ArchiveTabIButton]; | ||||
|     case ArchiveFileTypeNFC: | ||||
|         return tab_default_paths[ArchiveTabNFC]; | ||||
|     case ArchiveFileTypeSubOne: | ||||
|         return tab_default_paths[ArchiveTabSubOne]; | ||||
|     case ArchiveFileTypeSubGhz: | ||||
|         return tab_default_paths[ArchiveTabSubGhz]; | ||||
|     case ArchiveFileTypeLFRFID: | ||||
|         return tab_default_paths[ArchiveTabLFRFID]; | ||||
|     case ArchiveFileTypeIrda: | ||||
| @ -101,6 +104,13 @@ typedef struct { | ||||
|     EventType type; | ||||
| } AppEvent; | ||||
| 
 | ||||
| typedef enum { | ||||
|     FavoritesCheck, | ||||
|     FavoritesRead, | ||||
|     FavoritesDelete, | ||||
|     FavoritesRename, | ||||
| } FavActionsEnum; | ||||
| 
 | ||||
| typedef struct { | ||||
|     ArchiveTabEnum tab_id; | ||||
|     string_t name; | ||||
| @ -123,5 +133,6 @@ struct ArchiveApp { | ||||
|     TextInput* text_input; | ||||
| 
 | ||||
|     Storage* api; | ||||
|     FileWorker* file_worker; | ||||
|     ArchiveBrowser browser; | ||||
| }; | ||||
|  | ||||
| @ -4,7 +4,7 @@ static const char* ArchiveTabNames[] = { | ||||
|     [ArchiveTabFavorites] = "Favorites", | ||||
|     [ArchiveTabIButton] = "iButton", | ||||
|     [ArchiveTabNFC] = "NFC", | ||||
|     [ArchiveTabSubOne] = "SubGhz", | ||||
|     [ArchiveTabSubGhz] = "Sub-GHz", | ||||
|     [ArchiveTabLFRFID] = "RFID LF", | ||||
|     [ArchiveTabIrda] = "Infrared", | ||||
|     [ArchiveTabBrowser] = "Browser"}; | ||||
| @ -12,7 +12,7 @@ static const char* ArchiveTabNames[] = { | ||||
| static const Icon* ArchiveItemIcons[] = { | ||||
|     [ArchiveFileTypeIButton] = &I_ibutt_10px, | ||||
|     [ArchiveFileTypeNFC] = &I_Nfc_10px, | ||||
|     [ArchiveFileTypeSubOne] = &I_sub1_10px, | ||||
|     [ArchiveFileTypeSubGhz] = &I_sub1_10px, | ||||
|     [ArchiveFileTypeLFRFID] = &I_125_10px, | ||||
|     [ArchiveFileTypeIrda] = &I_ir_10px, | ||||
|     [ArchiveFileTypeFolder] = &I_dir_10px, | ||||
| @ -81,21 +81,21 @@ static void draw_list(Canvas* canvas, ArchiveViewModel* model) { | ||||
|     size_t array_size = files_array_size(model->files); | ||||
|     bool scrollbar = array_size > 4; | ||||
| 
 | ||||
|     for(size_t i = 0; i < MIN(array_size, MENU_ITEMS); ++i) { | ||||
|         string_t str_buff; | ||||
|         char cstr_buff[MAX_NAME_LEN]; | ||||
|     string_init(str_buff); | ||||
| 
 | ||||
|     for(size_t i = 0; i < MIN(array_size, MENU_ITEMS); ++i) { | ||||
|         size_t idx = CLAMP(i + model->list_offset, array_size, 0); | ||||
|         ArchiveFile_t* file = files_array_get(model->files, CLAMP(idx, array_size - 1, 0)); | ||||
| 
 | ||||
|         strlcpy(cstr_buff, string_get_cstr(file->name), string_size(file->name) + 1); | ||||
|         if(is_known_app(file->type)) archive_trim_file_ext(cstr_buff); | ||||
|         string_set_str(str_buff, cstr_buff); | ||||
|         string_init_set(str_buff, file->name); | ||||
|         string_right(str_buff, string_search_rchar(str_buff, '/') + 1); | ||||
|         strlcpy(cstr_buff, string_get_cstr(str_buff), string_size(str_buff) + 1); | ||||
| 
 | ||||
|         if(is_known_app(file->type)) { | ||||
|             archive_trim_file_ext(cstr_buff); | ||||
|         } | ||||
|         if(is_known_app(file->type)) archive_trim_file_ext(cstr_buff); | ||||
| 
 | ||||
|         string_clean(str_buff); | ||||
|         string_set_str(str_buff, cstr_buff); | ||||
| 
 | ||||
|         elements_string_fit_width(canvas, str_buff, scrollbar ? MAX_LEN_PX - 6 : MAX_LEN_PX); | ||||
| 
 | ||||
| @ -107,7 +107,7 @@ static void draw_list(Canvas* canvas, ArchiveViewModel* model) { | ||||
| 
 | ||||
|         canvas_draw_icon(canvas, 2, 16 + i * FRAME_HEIGHT, ArchiveItemIcons[file->type]); | ||||
|         canvas_draw_str(canvas, 15, 24 + i * FRAME_HEIGHT, string_get_cstr(str_buff)); | ||||
|         string_clean(str_buff); | ||||
|         string_clear(str_buff); | ||||
|     } | ||||
| 
 | ||||
|     if(scrollbar) { | ||||
| @ -117,8 +117,6 @@ static void draw_list(Canvas* canvas, ArchiveViewModel* model) { | ||||
|     if(model->menu) { | ||||
|         render_item_menu(canvas, model); | ||||
|     } | ||||
| 
 | ||||
|     string_clear(str_buff); | ||||
| } | ||||
| 
 | ||||
| static void archive_render_status_bar(Canvas* canvas, ArchiveViewModel* model) { | ||||
|  | ||||
| @ -14,7 +14,7 @@ | ||||
| typedef enum { | ||||
|     ArchiveFileTypeIButton, | ||||
|     ArchiveFileTypeNFC, | ||||
|     ArchiveFileTypeSubOne, | ||||
|     ArchiveFileTypeSubGhz, | ||||
|     ArchiveFileTypeLFRFID, | ||||
|     ArchiveFileTypeIrda, | ||||
|     ArchiveFileTypeFolder, | ||||
| @ -25,7 +25,7 @@ typedef enum { | ||||
| typedef enum { | ||||
|     ArchiveTabFavorites, | ||||
|     ArchiveTabLFRFID, | ||||
|     ArchiveTabSubOne, | ||||
|     ArchiveTabSubGhz, | ||||
|     ArchiveTabNFC, | ||||
|     ArchiveTabIButton, | ||||
|     ArchiveTabIrda, | ||||
|  | ||||
| @ -46,7 +46,7 @@ void cli_stdout_callback(void* _cookie, const char* data, size_t size) { | ||||
|     furi_hal_vcp_tx((const uint8_t*)data, size); | ||||
| } | ||||
| 
 | ||||
| void cli_write(Cli* cli, uint8_t* buffer, size_t size) { | ||||
| void cli_write(Cli* cli, const uint8_t* buffer, size_t size) { | ||||
|     return furi_hal_vcp_tx(buffer, size); | ||||
| } | ||||
| 
 | ||||
| @ -300,6 +300,7 @@ void cli_process_input(Cli* cli) { | ||||
|     if(c == CliSymbolAsciiTab) { | ||||
|         cli_handle_autocomplete(cli); | ||||
|     } else if(c == CliSymbolAsciiSOH) { | ||||
|         osDelay(33); // We are too fast, Minicom is not ready yet
 | ||||
|         cli_motd(); | ||||
|         cli_prompt(cli); | ||||
|     } else if(c == CliSymbolAsciiETX) { | ||||
|  | ||||
| @ -88,7 +88,7 @@ bool cli_cmd_interrupt_received(Cli* cli); | ||||
|  * @param size - size of buffer in bytes | ||||
|  * @return bytes written | ||||
|  */ | ||||
| void cli_write(Cli* cli, uint8_t* buffer, size_t size); | ||||
| void cli_write(Cli* cli, const uint8_t* buffer, size_t size); | ||||
| 
 | ||||
| /* Read character
 | ||||
|  * @param cli - Cli instance | ||||
|  | ||||
| @ -22,42 +22,6 @@ typedef struct { | ||||
|     EventType type; | ||||
| } KeypadTestEvent; | ||||
| 
 | ||||
| static const char* keypad_test_get_key_name(InputKey key) { | ||||
|     switch(key) { | ||||
|     case InputKeyOk: | ||||
|         return "Ok"; | ||||
|     case InputKeyBack: | ||||
|         return "Back"; | ||||
|     case InputKeyLeft: | ||||
|         return "Left"; | ||||
|     case InputKeyRight: | ||||
|         return "Right"; | ||||
|     case InputKeyUp: | ||||
|         return "Up"; | ||||
|     case InputKeyDown: | ||||
|         return "Down"; | ||||
|     default: | ||||
|         return "Unknown"; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| static const char* keypad_test_get_type_name(InputType type) { | ||||
|     switch(type) { | ||||
|     case InputTypePress: | ||||
|         return "Press"; | ||||
|     case InputTypeRelease: | ||||
|         return "Release"; | ||||
|     case InputTypeShort: | ||||
|         return "Short"; | ||||
|     case InputTypeLong: | ||||
|         return "Long"; | ||||
|     case InputTypeRepeat: | ||||
|         return "Repeat"; | ||||
|     default: | ||||
|         return "Unknown"; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| static void keypad_test_reset_state(KeypadTestState* state) { | ||||
|     state->left = 0; | ||||
|     state->right = 0; | ||||
| @ -139,8 +103,8 @@ int32_t keypad_test_app(void* p) { | ||||
|                 FURI_LOG_I( | ||||
|                     "KeypadTest", | ||||
|                     "key: %s type: %s", | ||||
|                     keypad_test_get_key_name(event.input.key), | ||||
|                     keypad_test_get_type_name(event.input.type)); | ||||
|                     input_get_key_name(event.input.key), | ||||
|                     input_get_type_name(event.input.type)); | ||||
| 
 | ||||
|                 if(event.input.type == InputTypeLong && event.input.key == InputKeyBack) { | ||||
|                     release_mutex(&state_mutex, state); | ||||
|  | ||||
| @ -11,6 +11,8 @@ struct ViewHolder { | ||||
| 
 | ||||
|     BackCallback back_callback; | ||||
|     void* back_context; | ||||
| 
 | ||||
|     uint8_t ongoing_input; | ||||
| }; | ||||
| 
 | ||||
| static void view_holder_draw_callback(Canvas* canvas, void* context); | ||||
| @ -92,6 +94,7 @@ void view_holder_start(ViewHolder* view_holder) { | ||||
| } | ||||
| 
 | ||||
| void view_holder_stop(ViewHolder* view_holder) { | ||||
|     while(view_holder->ongoing_input) osDelay(1); | ||||
|     view_port_enabled_set(view_holder->view_port, false); | ||||
| } | ||||
| 
 | ||||
| @ -114,6 +117,21 @@ static void view_holder_draw_callback(Canvas* canvas, void* context) { | ||||
| 
 | ||||
| static void view_holder_input_callback(InputEvent* event, void* context) { | ||||
|     ViewHolder* view_holder = context; | ||||
| 
 | ||||
|     uint8_t key_bit = (1 << event->key); | ||||
|     if(event->type == InputTypePress) { | ||||
|         view_holder->ongoing_input |= key_bit; | ||||
|     } else if(event->type == InputTypeRelease) { | ||||
|         view_holder->ongoing_input &= ~key_bit; | ||||
|     } else if(!(view_holder->ongoing_input & key_bit)) { | ||||
|         FURI_LOG_W( | ||||
|             "ViewHolder", | ||||
|             "non-complementary input, discarding key: %s, type: %s", | ||||
|             input_get_key_name(event->key), | ||||
|             input_get_type_name(event->type)); | ||||
|         return; | ||||
|     } | ||||
| 
 | ||||
|     bool is_consumed = false; | ||||
| 
 | ||||
|     if(view_holder->view) { | ||||
|  | ||||
| @ -2,143 +2,134 @@ | ||||
| #include <furi-hal.h> | ||||
| 
 | ||||
| #include <gui/gui.h> | ||||
| #include <input/input.h> | ||||
| #include <notification/notification-messages.h> | ||||
| 
 | ||||
| typedef struct { | ||||
|     const char* name; | ||||
|     GpioPin pin; | ||||
|     const GpioPin* pin; | ||||
| } GpioItem; | ||||
| 
 | ||||
| const GpioItem GPIO_PINS[] = { | ||||
|     {"1.2: PA7", {GPIOA, GPIO_PIN_7}}, | ||||
|     {"1.3: PA6", {GPIOA, GPIO_PIN_6}}, | ||||
|     {"1.4: PA4", {GPIOA, GPIO_PIN_4}}, | ||||
|     {"1.5: PB3", {GPIOB, GPIO_PIN_3}}, | ||||
|     {"1.6: PB2", {GPIOB, GPIO_PIN_2}}, | ||||
|     {"1.7: PC3", {GPIOC, GPIO_PIN_3}}, | ||||
| 
 | ||||
|     {"2.7: PC1", {GPIOC, GPIO_PIN_1}}, | ||||
|     {"2.8: PC0", {GPIOC, GPIO_PIN_0}}, | ||||
| static const GpioItem GPIO_PINS[] = { | ||||
|     {"1.2: PA7", &gpio_ext_pa7}, | ||||
|     {"1.3: PA6", &gpio_ext_pa6}, | ||||
|     {"1.4: PA4", &gpio_ext_pa4}, | ||||
|     {"1.5: PB3", &gpio_ext_pb3}, | ||||
|     {"1.6: PB2", &gpio_ext_pb2}, | ||||
|     {"1.7: PC3", &gpio_ext_pc3}, | ||||
|     {"2.7: PC1", &gpio_ext_pc1}, | ||||
|     {"2.8: PC0", &gpio_ext_pc0}, | ||||
| }; | ||||
| 
 | ||||
| typedef enum { | ||||
|     EventTypeTick, | ||||
|     EventTypeKey, | ||||
| } EventType; | ||||
| 
 | ||||
| typedef struct { | ||||
|     union { | ||||
|         InputEvent input; | ||||
|     } value; | ||||
|     EventType type; | ||||
| } AppEvent; | ||||
| static const size_t GPIO_PINS_COUNT = sizeof(GPIO_PINS) / sizeof(GPIO_PINS[0]); | ||||
| 
 | ||||
| typedef struct { | ||||
|     osMessageQueueId_t input_queue; | ||||
|     uint8_t gpio_index; | ||||
| } State; | ||||
|     ViewPort* view_port; | ||||
|     Gui* gui; | ||||
|     NotificationApp* notification; | ||||
| } GpioTest; | ||||
| 
 | ||||
| static void render_callback(Canvas* canvas, void* ctx) { | ||||
|     State* state = (State*)acquire_mutex((ValueMutex*)ctx, 25); | ||||
| static void gpio_test_render_callback(Canvas* canvas, void* ctx) { | ||||
|     GpioTest* gpio_test = ctx; | ||||
| 
 | ||||
|     canvas_clear(canvas); | ||||
|     canvas_set_color(canvas, ColorBlack); | ||||
|     canvas_set_font(canvas, FontPrimary); | ||||
|     canvas_draw_str(canvas, 2, 10, "GPIO Control"); | ||||
|     canvas_set_font(canvas, FontSecondary); | ||||
|     canvas_draw_str(canvas, 2, 25, GPIO_PINS[state->gpio_index].name); | ||||
| 
 | ||||
|     release_mutex((ValueMutex*)ctx, state); | ||||
|     canvas_draw_str(canvas, 2, 25, GPIO_PINS[gpio_test->gpio_index].name); | ||||
| } | ||||
| 
 | ||||
| static void input_callback(InputEvent* input_event, void* ctx) { | ||||
|     osMessageQueueId_t event_queue = ctx; | ||||
| static void gpio_test_input_callback(InputEvent* input_event, void* ctx) { | ||||
|     GpioTest* gpio_test = ctx; | ||||
| 
 | ||||
|     AppEvent event; | ||||
|     event.type = EventTypeKey; | ||||
|     event.value.input = *input_event; | ||||
|     osMessageQueuePut(event_queue, &event, 0, 0); | ||||
|     osMessageQueuePut(gpio_test->input_queue, input_event, 0, 0); | ||||
| } | ||||
| 
 | ||||
| static void gpio_test_configure_pins(GpioMode mode) { | ||||
|     for(size_t i = 0; i < GPIO_PINS_COUNT; i++) { | ||||
|         hal_gpio_write(GPIO_PINS[i].pin, false); | ||||
|         hal_gpio_init(GPIO_PINS[i].pin, mode, GpioPullNo, GpioSpeedLow); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| GpioTest* gpio_test_alloc() { | ||||
|     GpioTest* instance = furi_alloc(sizeof(GpioTest)); | ||||
| 
 | ||||
|     gpio_test_configure_pins(GpioModeOutputPushPull); | ||||
| 
 | ||||
|     instance->input_queue = osMessageQueueNew(8, sizeof(InputEvent), NULL); | ||||
|     furi_check(instance->input_queue); | ||||
| 
 | ||||
|     instance->view_port = view_port_alloc(); | ||||
|     view_port_draw_callback_set(instance->view_port, gpio_test_render_callback, instance); | ||||
|     view_port_input_callback_set(instance->view_port, gpio_test_input_callback, instance); | ||||
| 
 | ||||
|     instance->gui = furi_record_open("gui"); | ||||
|     gui_add_view_port(instance->gui, instance->view_port, GuiLayerFullscreen); | ||||
| 
 | ||||
|     instance->notification = furi_record_open("notification"); | ||||
| 
 | ||||
|     return instance; | ||||
| } | ||||
| 
 | ||||
| void gpio_test_free(GpioTest* instance) { | ||||
|     furi_assert(instance); | ||||
| 
 | ||||
|     furi_record_close("notification"); | ||||
| 
 | ||||
|     view_port_enabled_set(instance->view_port, false); | ||||
|     gui_remove_view_port(instance->gui, instance->view_port); | ||||
|     furi_record_close("gui"); | ||||
| 
 | ||||
|     view_port_free(instance->view_port); | ||||
| 
 | ||||
|     osMessageQueueDelete(instance->input_queue); | ||||
| 
 | ||||
|     gpio_test_configure_pins(GpioModeAnalog); | ||||
| 
 | ||||
|     free(instance); | ||||
| } | ||||
| 
 | ||||
| int32_t gpio_test_app(void* p) { | ||||
|     osMessageQueueId_t event_queue = osMessageQueueNew(8, sizeof(AppEvent), NULL); | ||||
|     furi_check(event_queue); | ||||
|     GpioTest* gpio_test = gpio_test_alloc(); | ||||
| 
 | ||||
|     State _state; | ||||
|     _state.gpio_index = 0; | ||||
| 
 | ||||
|     ValueMutex state_mutex; | ||||
|     if(!init_mutex(&state_mutex, &_state, sizeof(State))) { | ||||
|         printf("[gpio-tester] cannot create mutex\r\n"); | ||||
|         return 255; | ||||
|     InputEvent event; | ||||
|     while(osMessageQueueGet(gpio_test->input_queue, &event, NULL, osWaitForever) == osOK) { | ||||
|         if(event.type == InputTypeShort) { | ||||
|             if(event.key == InputKeyBack) { | ||||
|                 notification_message(gpio_test->notification, &sequence_reset_green); | ||||
|                 break; | ||||
|             } | ||||
| 
 | ||||
|     ViewPort* view_port = view_port_alloc(); | ||||
| 
 | ||||
|     view_port_draw_callback_set(view_port, render_callback, &state_mutex); | ||||
|     view_port_input_callback_set(view_port, input_callback, event_queue); | ||||
| 
 | ||||
|     // Open GUI and register view_port
 | ||||
|     Gui* gui = furi_record_open("gui"); | ||||
|     gui_add_view_port(gui, view_port, GuiLayerFullscreen); | ||||
| 
 | ||||
|     NotificationApp* notification = furi_record_open("notification"); | ||||
| 
 | ||||
|     // configure pin
 | ||||
|     for(uint8_t i = 0; i < sizeof(GPIO_PINS) / sizeof(GPIO_PINS[0]); i++) { | ||||
|         hal_gpio_init( | ||||
|             (GpioPin*)&GPIO_PINS[i].pin, GpioModeOutputPushPull, GpioPullNo, GpioSpeedLow); | ||||
|             if(event.key == InputKeyRight) { | ||||
|                 if(gpio_test->gpio_index < (GPIO_PINS_COUNT - 1)) { | ||||
|                     gpio_test->gpio_index++; | ||||
|                 } | ||||
|             } | ||||
| 
 | ||||
|     AppEvent event; | ||||
|     while(1) { | ||||
|         osStatus_t event_status = osMessageQueueGet(event_queue, &event, NULL, osWaitForever); | ||||
|         State* state = (State*)acquire_mutex_block(&state_mutex); | ||||
|             if(event.key == InputKeyLeft) { | ||||
|                 if(gpio_test->gpio_index > 0) { | ||||
|                     gpio_test->gpio_index--; | ||||
|                 } | ||||
|             } | ||||
|         } else { | ||||
|             if(event.key == InputKeyOk) { | ||||
|                 if(event.type == InputTypePress) { | ||||
|                     hal_gpio_write(GPIO_PINS[gpio_test->gpio_index].pin, true); | ||||
|                     notification_message(gpio_test->notification, &sequence_set_green_255); | ||||
|                 } else if(event.type == InputTypeRelease) { | ||||
|                     hal_gpio_write(GPIO_PINS[gpio_test->gpio_index].pin, false); | ||||
|                     notification_message(gpio_test->notification, &sequence_reset_green); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         if(event_status == osOK) { | ||||
|             if(event.type == EventTypeKey) { | ||||
|                 if(event.value.input.type == InputTypeShort && | ||||
|                    event.value.input.key == InputKeyBack) { | ||||
|                     printf("[gpio-tester] bye!\r\n"); | ||||
|                     notification_message(notification, &sequence_reset_green); | ||||
|                     furi_record_close("notification"); | ||||
|         view_port_update(gpio_test->view_port); | ||||
|     } | ||||
| 
 | ||||
|                     view_port_enabled_set(view_port, false); | ||||
|                     gui_remove_view_port(gui, view_port); | ||||
|                     view_port_free(view_port); | ||||
| 
 | ||||
|                     return 0; | ||||
|                 } | ||||
| 
 | ||||
|                 if(event.value.input.type == InputTypeShort && | ||||
|                    event.value.input.key == InputKeyRight) { | ||||
|                     if(state->gpio_index < (sizeof(GPIO_PINS) / sizeof(GPIO_PINS[0]) - 1)) { | ||||
|                         state->gpio_index++; | ||||
|                     } | ||||
|                 } | ||||
| 
 | ||||
|                 if(event.value.input.type == InputTypeShort && | ||||
|                    event.value.input.key == InputKeyLeft) { | ||||
|                     if(state->gpio_index > 0) { | ||||
|                         state->gpio_index--; | ||||
|                     } | ||||
|                 } | ||||
| 
 | ||||
|                 if(event.value.input.key == InputKeyOk) { | ||||
|                     if(event.value.input.type == InputTypePress) { | ||||
|                         hal_gpio_write((GpioPin*)&GPIO_PINS[state->gpio_index].pin, true); | ||||
|                         notification_message(notification, &sequence_set_green_255); | ||||
|                     } else if(event.value.input.type == InputTypeRelease) { | ||||
|                         hal_gpio_write((GpioPin*)&GPIO_PINS[state->gpio_index].pin, false); | ||||
|                         notification_message(notification, &sequence_reset_green); | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         release_mutex(&state_mutex, state); | ||||
|         view_port_update(view_port); | ||||
|     } | ||||
|     gpio_test_free(gpio_test); | ||||
| 
 | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| @ -309,14 +309,17 @@ void canvas_set_orientation(Canvas* canvas, CanvasOrientation orientation) { | ||||
|     furi_assert(canvas); | ||||
|     if(canvas->orientation != orientation) { | ||||
|         canvas->orientation = orientation; | ||||
|         if(canvas->orientation == CanvasOrientationHorizontal) | ||||
|         if(canvas->orientation == CanvasOrientationHorizontal) { | ||||
|             FURI_SWAP(canvas->width, canvas->height); | ||||
|             u8g2_SetDisplayRotation(&canvas->fb, U8G2_R0); | ||||
|         else if(canvas->orientation == CanvasOrientationVertical) | ||||
|         } else if(canvas->orientation == CanvasOrientationVertical) { | ||||
|             FURI_SWAP(canvas->width, canvas->height); | ||||
|             u8g2_SetDisplayRotation(&canvas->fb, U8G2_R3); | ||||
|         else | ||||
|         } else { | ||||
|             furi_assert(0); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| CanvasOrientation canvas_get_orientation(const Canvas* canvas) { | ||||
|     return canvas->orientation; | ||||
|  | ||||
| @ -1,40 +1,5 @@ | ||||
| #include "gui_i.h" | ||||
| 
 | ||||
| static void gui_rotate_buttons(InputEvent* event) { | ||||
|     switch(event->key) { | ||||
|     case InputKeyUp: | ||||
|         event->key = InputKeyRight; | ||||
|         break; | ||||
|     case InputKeyDown: | ||||
|         event->key = InputKeyLeft; | ||||
|         break; | ||||
|     case InputKeyRight: | ||||
|         event->key = InputKeyDown; | ||||
|         break; | ||||
|     case InputKeyLeft: | ||||
|         event->key = InputKeyUp; | ||||
|         break; | ||||
|     default: | ||||
|         break; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| static void gui_setup_fs_orientation(const ViewPort* view_port, Canvas* canvas) { | ||||
|     ViewPortOrientation view_port_orientation = view_port_get_orientation(view_port); | ||||
|     CanvasOrientation canvas_orientation = canvas_get_orientation(canvas); | ||||
|     if(view_port_orientation == ViewPortOrientationHorizontal) { | ||||
|         canvas_frame_set(canvas, 0, 0, GUI_DISPLAY_WIDTH, GUI_DISPLAY_HEIGHT); | ||||
|         if(canvas_orientation != CanvasOrientationHorizontal) { | ||||
|             canvas_set_orientation(canvas, CanvasOrientationHorizontal); | ||||
|         } | ||||
|     } else if(view_port_orientation == ViewPortOrientationVertical) { | ||||
|         canvas_frame_set(canvas, 0, 0, GUI_DISPLAY_HEIGHT, GUI_DISPLAY_WIDTH); | ||||
|         if(canvas_orientation != CanvasOrientationVertical) { | ||||
|             canvas_set_orientation(canvas, CanvasOrientationVertical); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| ViewPort* gui_view_port_find_enabled(ViewPortArray_t array) { | ||||
|     // Iterating backward
 | ||||
|     ViewPortArray_it_t it; | ||||
| @ -66,9 +31,10 @@ void gui_input_events_callback(const void* value, void* ctx) { | ||||
| 
 | ||||
| // Only Fullscreen supports vertical display for now
 | ||||
| bool gui_redraw_fs(Gui* gui) { | ||||
|     canvas_set_orientation(gui->canvas, CanvasOrientationHorizontal); | ||||
|     canvas_frame_set(gui->canvas, 0, 0, GUI_DISPLAY_WIDTH, GUI_DISPLAY_HEIGHT); | ||||
|     ViewPort* view_port = gui_view_port_find_enabled(gui->layers[GuiLayerFullscreen]); | ||||
|     if(view_port) { | ||||
|         gui_setup_fs_orientation(view_port, gui->canvas); | ||||
|         view_port_draw(view_port, gui->canvas); | ||||
|         return true; | ||||
|     } else { | ||||
| @ -216,20 +182,53 @@ void gui_input(Gui* gui, InputEvent* input_event) { | ||||
|     furi_assert(gui); | ||||
|     furi_assert(input_event); | ||||
| 
 | ||||
|     // Check input complementarity
 | ||||
|     uint8_t key_bit = (1 << input_event->key); | ||||
|     if(input_event->type == InputTypeRelease) { | ||||
|         gui->ongoing_input &= ~key_bit; | ||||
|     } else if(input_event->type == InputTypePress) { | ||||
|         gui->ongoing_input |= key_bit; | ||||
|     } else if(!(gui->ongoing_input & key_bit)) { | ||||
|         FURI_LOG_W( | ||||
|             "Gui", | ||||
|             "non-complementary input, discarding key: %s type: %s, sequence: %p", | ||||
|             input_get_key_name(input_event->key), | ||||
|             input_get_type_name(input_event->type), | ||||
|             input_event->sequence); | ||||
|         return; | ||||
|     } | ||||
| 
 | ||||
|     gui_lock(gui); | ||||
| 
 | ||||
|     ViewPort* view_port; | ||||
| 
 | ||||
|     view_port = gui_view_port_find_enabled(gui->layers[GuiLayerFullscreen]); | ||||
|     ViewPort* view_port = gui_view_port_find_enabled(gui->layers[GuiLayerFullscreen]); | ||||
|     if(!view_port) view_port = gui_view_port_find_enabled(gui->layers[GuiLayerMain]); | ||||
|     if(!view_port) view_port = gui_view_port_find_enabled(gui->layers[GuiLayerNone]); | ||||
| 
 | ||||
|     if(view_port) { | ||||
|         if(view_port_get_orientation(view_port) == ViewPortOrientationVertical) { | ||||
|             gui_rotate_buttons(input_event); | ||||
|     if(!(gui->ongoing_input & ~key_bit) && input_event->type == InputTypePress) { | ||||
|         gui->ongoing_input_view_port = view_port; | ||||
|     } | ||||
| 
 | ||||
|     if(view_port && view_port == gui->ongoing_input_view_port) { | ||||
|         view_port_input(view_port, input_event); | ||||
|     } else if(gui->ongoing_input_view_port && input_event->type == InputTypeRelease) { | ||||
|         FURI_LOG_W( | ||||
|             "Gui", | ||||
|             "ViewPort changed while key press %p -> %p. Sending key: %s, type: %s, sequence: %p to previous view port", | ||||
|             gui->ongoing_input_view_port, | ||||
|             view_port, | ||||
|             input_get_key_name(input_event->key), | ||||
|             input_get_type_name(input_event->type), | ||||
|             input_event->sequence); | ||||
|         view_port_input(gui->ongoing_input_view_port, input_event); | ||||
|     } else { | ||||
|         FURI_LOG_W( | ||||
|             "Gui", | ||||
|             "ViewPort changed while key press %p -> %p. Discarding key: %s, type: %s, sequence: %p", | ||||
|             gui->ongoing_input_view_port, | ||||
|             view_port, | ||||
|             input_get_key_name(input_event->key), | ||||
|             input_get_type_name(input_event->type), | ||||
|             input_event->sequence); | ||||
|     } | ||||
| 
 | ||||
|     gui_unlock(gui); | ||||
| @ -251,7 +250,7 @@ void gui_cli_screen_stream_callback(uint8_t* data, size_t size, void* context) { | ||||
|     furi_assert(context); | ||||
| 
 | ||||
|     Gui* gui = context; | ||||
|     uint8_t magic[] = {0xF0, 0xE1, 0xD2, 0xC3}; | ||||
|     const uint8_t magic[] = {0xF0, 0xE1, 0xD2, 0xC3}; | ||||
|     cli_write(gui->cli, magic, sizeof(magic)); | ||||
|     cli_write(gui->cli, data, size); | ||||
| } | ||||
| @ -329,6 +328,10 @@ void gui_remove_view_port(Gui* gui, ViewPort* view_port) { | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     if(gui->ongoing_input_view_port == view_port) { | ||||
|         gui->ongoing_input_view_port = NULL; | ||||
|     } | ||||
| 
 | ||||
|     gui_unlock(gui); | ||||
| } | ||||
| 
 | ||||
|  | ||||
| @ -35,14 +35,19 @@ struct Gui { | ||||
|     // Thread and lock
 | ||||
|     osThreadId_t thread; | ||||
|     osMutexId_t mutex; | ||||
| 
 | ||||
|     // Layers and Canvas
 | ||||
|     ViewPortArray_t layers[GuiLayerMAX]; | ||||
|     Canvas* canvas; | ||||
|     GuiCanvasCommitCallback canvas_callback; | ||||
|     void* canvas_callback_context; | ||||
| 
 | ||||
|     // Input
 | ||||
|     osMessageQueueId_t input_queue; | ||||
|     PubSub* input_events; | ||||
|     uint8_t ongoing_input; | ||||
|     ViewPort* ongoing_input_view_port; | ||||
| 
 | ||||
|     // Cli
 | ||||
|     Cli* cli; | ||||
| }; | ||||
|  | ||||
| @ -150,3 +150,15 @@ void widget_add_icon_element(Widget* widget, uint8_t x, uint8_t y, const Icon* i | ||||
|     WidgetElement* icon_element = widget_element_icon_create(x, y, icon); | ||||
|     widget_add_element(widget, icon_element); | ||||
| } | ||||
| 
 | ||||
| void widget_add_frame_element( | ||||
|     Widget* widget, | ||||
|     uint8_t x, | ||||
|     uint8_t y, | ||||
|     uint8_t width, | ||||
|     uint8_t height, | ||||
|     uint8_t radius) { | ||||
|     furi_assert(widget); | ||||
|     WidgetElement* frame_element = widget_element_frame_create(x, y, width, height, radius); | ||||
|     widget_add_element(widget, frame_element); | ||||
| } | ||||
|  | ||||
| @ -59,8 +59,24 @@ void widget_add_button_element( | ||||
| 
 | ||||
| /** Add Icon Element
 | ||||
|  * @param widget Widget instance | ||||
|  * @param x - x coordinate | ||||
|  * @param y - y coordinate | ||||
|  * @param x top left x coordinate | ||||
|  * @param y top left y coordinate | ||||
|  * @param icon Icon instance | ||||
|  */ | ||||
| void widget_add_icon_element(Widget* widget, uint8_t x, uint8_t y, const Icon* icon); | ||||
| 
 | ||||
| /** Add Frame Element
 | ||||
|  * @param widget Widget instance | ||||
|  * @param x top left x coordinate | ||||
|  * @param y top left y coordinate | ||||
|  * @param width frame width | ||||
|  * @param height frame height | ||||
|  * @param radius frame radius | ||||
|  */ | ||||
| void widget_add_frame_element( | ||||
|     Widget* widget, | ||||
|     uint8_t x, | ||||
|     uint8_t y, | ||||
|     uint8_t width, | ||||
|     uint8_t height, | ||||
|     uint8_t radius); | ||||
|  | ||||
							
								
								
									
										48
									
								
								applications/gui/modules/widget_elements/widget_element_frame.c
									
									
									
									
									
										Executable file
									
								
							
							
						
						| @ -0,0 +1,48 @@ | ||||
| #include "widget_element_i.h" | ||||
| 
 | ||||
| typedef struct { | ||||
|     uint8_t x; | ||||
|     uint8_t y; | ||||
|     uint8_t width; | ||||
|     uint8_t height; | ||||
|     uint8_t radius; | ||||
| } GuiFrameModel; | ||||
| 
 | ||||
| static void gui_frame_draw(Canvas* canvas, WidgetElement* element) { | ||||
|     furi_assert(canvas); | ||||
|     furi_assert(element); | ||||
|     GuiFrameModel* model = element->model; | ||||
|     canvas_draw_rframe(canvas, model->x, model->y, model->width, model->height, model->radius); | ||||
| } | ||||
| 
 | ||||
| static void gui_frame_free(WidgetElement* gui_frame) { | ||||
|     furi_assert(gui_frame); | ||||
| 
 | ||||
|     free(gui_frame->model); | ||||
|     free(gui_frame); | ||||
| } | ||||
| 
 | ||||
| WidgetElement* widget_element_frame_create( | ||||
|     uint8_t x, | ||||
|     uint8_t y, | ||||
|     uint8_t width, | ||||
|     uint8_t height, | ||||
|     uint8_t radius) { | ||||
|     // Allocate and init model
 | ||||
|     GuiFrameModel* model = furi_alloc(sizeof(GuiFrameModel)); | ||||
|     model->x = x; | ||||
|     model->y = y; | ||||
|     model->width = width; | ||||
|     model->height = height; | ||||
|     model->radius = radius; | ||||
| 
 | ||||
|     // Allocate and init Element
 | ||||
|     WidgetElement* gui_frame = furi_alloc(sizeof(WidgetElement)); | ||||
|     gui_frame->parent = NULL; | ||||
|     gui_frame->input = NULL; | ||||
|     gui_frame->draw = gui_frame_draw; | ||||
|     gui_frame->free = gui_frame_free; | ||||
|     gui_frame->model = model; | ||||
| 
 | ||||
|     return gui_frame; | ||||
| } | ||||
| @ -44,5 +44,13 @@ WidgetElement* widget_element_button_create( | ||||
|     ButtonCallback callback, | ||||
|     void* context); | ||||
| 
 | ||||
| /* Create icon element element */ | ||||
| /* Create icon element */ | ||||
| WidgetElement* widget_element_icon_create(uint8_t x, uint8_t y, const Icon* icon); | ||||
| 
 | ||||
| /* Create frame element */ | ||||
| WidgetElement* widget_element_frame_create( | ||||
|     uint8_t x, | ||||
|     uint8_t y, | ||||
|     uint8_t width, | ||||
|     uint8_t height, | ||||
|     uint8_t radius); | ||||
| @ -1,5 +1,4 @@ | ||||
| #include "widget_element_i.h" | ||||
| #include <m-string.h> | ||||
| 
 | ||||
| typedef struct { | ||||
|     uint8_t x; | ||||
|  | ||||
| @ -35,11 +35,6 @@ void view_set_previous_callback(View* view, ViewNavigationCallback callback) { | ||||
|     view->previous_callback = callback; | ||||
| } | ||||
| 
 | ||||
| void view_set_next_callback(View* view, ViewNavigationCallback callback) { | ||||
|     furi_assert(view); | ||||
|     view->next_callback = callback; | ||||
| } | ||||
| 
 | ||||
| void view_set_enter_callback(View* view, ViewCallback callback) { | ||||
|     furi_assert(view); | ||||
|     view->enter_callback = callback; | ||||
| @ -169,15 +164,6 @@ uint32_t view_previous(View* view) { | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| uint32_t view_next(View* view) { | ||||
|     furi_assert(view); | ||||
|     if(view->next_callback) { | ||||
|         return view->next_callback(view->context); | ||||
|     } else { | ||||
|         return VIEW_IGNORE; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void view_enter(View* view) { | ||||
|     furi_assert(view); | ||||
|     if(view->enter_callback) view->enter_callback(view->context); | ||||
|  | ||||
| @ -14,11 +14,6 @@ extern "C" { | ||||
| #define VIEW_NONE 0xFFFFFFFF | ||||
| /* Ignore navigation event */ | ||||
| #define VIEW_IGNORE 0xFFFFFFFE | ||||
| /* Deatch from gui, deallocate Views and ViewDispatcher
 | ||||
|  * BE SUPER CAREFUL, deallocation happens automatically on GUI thread | ||||
|  * You ARE NOT owning ViewDispatcher and Views instances | ||||
|  */ | ||||
| #define VIEW_DESTROY 0xFFFFFFFA | ||||
| 
 | ||||
| typedef enum { | ||||
|     ViewOrientationHorizontal, | ||||
| @ -119,12 +114,6 @@ void view_set_custom_callback(View* view, ViewCustomCallback callback); | ||||
|  */ | ||||
| void view_set_previous_callback(View* view, ViewNavigationCallback callback); | ||||
| 
 | ||||
| /* Set Navigation Next callback
 | ||||
|  * @param view, pointer to View | ||||
|  * @param callback, input callback | ||||
|  */ | ||||
| void view_set_next_callback(View* view, ViewNavigationCallback callback); | ||||
| 
 | ||||
| /* Set Enter callback
 | ||||
|  * @param view, pointer to View | ||||
|  * @param callback, callback | ||||
|  | ||||
							
								
								
									
										57
									
								
								applications/gui/view_dispatcher.c
									
									
									
									
									
										
										
										Executable file → Normal file
									
								
							
							
						
						| @ -37,7 +37,7 @@ void view_dispatcher_free(ViewDispatcher* view_dispatcher) { | ||||
| void view_dispatcher_enable_queue(ViewDispatcher* view_dispatcher) { | ||||
|     furi_assert(view_dispatcher); | ||||
|     furi_assert(view_dispatcher->queue == NULL); | ||||
|     view_dispatcher->queue = osMessageQueueNew(8, sizeof(ViewDispatcherMessage), NULL); | ||||
|     view_dispatcher->queue = osMessageQueueNew(16, sizeof(ViewDispatcherMessage), NULL); | ||||
| } | ||||
| 
 | ||||
| void view_dispatcher_set_event_callback_context(ViewDispatcher* view_dispatcher, void* context) { | ||||
| @ -91,6 +91,19 @@ void view_dispatcher_run(ViewDispatcher* view_dispatcher) { | ||||
|             view_dispatcher_handle_custom_event(view_dispatcher, message.custom_event); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     // Wait till all input events delivered
 | ||||
|     while(view_dispatcher->ongoing_input) { | ||||
|         osMessageQueueGet(view_dispatcher->queue, &message, NULL, osWaitForever); | ||||
|         if(message.type == ViewDispatcherMessageTypeInput) { | ||||
|             uint8_t key_bit = (1 << message.input.key); | ||||
|             if(message.input.type == InputTypePress) { | ||||
|                 view_dispatcher->ongoing_input |= key_bit; | ||||
|             } else if(message.input.type == InputTypeRelease) { | ||||
|                 view_dispatcher->ongoing_input &= ~key_bit; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void view_dispatcher_stop(ViewDispatcher* view_dispatcher) { | ||||
| @ -136,6 +149,10 @@ void view_dispatcher_remove_view(ViewDispatcher* view_dispatcher, uint32_t view_ | ||||
|     if(view_dispatcher->current_view == view) { | ||||
|         view_dispatcher_set_current_view(view_dispatcher, NULL); | ||||
|     } | ||||
|     // Check if view is recieving input
 | ||||
|     if(view_dispatcher->ongoing_input_view == view) { | ||||
|         view_dispatcher->ongoing_input_view = NULL; | ||||
|     } | ||||
|     // Remove view
 | ||||
|     ViewDict_erase(view_dispatcher->views, view_id); | ||||
| 
 | ||||
| @ -153,8 +170,6 @@ void view_dispatcher_switch_to_view(ViewDispatcher* view_dispatcher, uint32_t vi | ||||
|     if(view_id == VIEW_NONE) { | ||||
|         view_dispatcher_set_current_view(view_dispatcher, NULL); | ||||
|     } else if(view_id == VIEW_IGNORE) { | ||||
|     } else if(view_id == VIEW_DESTROY) { | ||||
|         view_dispatcher_free(view_dispatcher); | ||||
|     } else { | ||||
|         View** view_pp = ViewDict_get(view_dispatcher->views, view_id); | ||||
|         furi_check(view_pp != NULL); | ||||
| @ -202,6 +217,29 @@ void view_dispatcher_input_callback(InputEvent* event, void* context) { | ||||
| } | ||||
| 
 | ||||
| void view_dispatcher_handle_input(ViewDispatcher* view_dispatcher, InputEvent* event) { | ||||
|     // Check input complementarity
 | ||||
|     uint8_t key_bit = (1 << event->key); | ||||
|     if(event->type == InputTypePress) { | ||||
|         view_dispatcher->ongoing_input |= key_bit; | ||||
|     } else if(event->type == InputTypeRelease) { | ||||
|         view_dispatcher->ongoing_input &= ~key_bit; | ||||
|     } else if(!(view_dispatcher->ongoing_input & key_bit)) { | ||||
|         FURI_LOG_W( | ||||
|             "ViewDispatcher", | ||||
|             "non-complementary input, discarding key: %s, type: %s, sequence: %p", | ||||
|             input_get_key_name(event->key), | ||||
|             input_get_type_name(event->type), | ||||
|             event->sequence); | ||||
|         return; | ||||
|     } | ||||
| 
 | ||||
|     // Set ongoing input view if this is event is first press event
 | ||||
|     if(!(view_dispatcher->ongoing_input & ~key_bit) && event->type == InputTypePress) { | ||||
|         view_dispatcher->ongoing_input_view = view_dispatcher->current_view; | ||||
|     } | ||||
| 
 | ||||
|     // Deliver event
 | ||||
|     if(view_dispatcher->ongoing_input_view == view_dispatcher->current_view) { | ||||
|         bool is_consumed = false; | ||||
|         if(view_dispatcher->current_view) { | ||||
|             is_consumed = view_input(view_dispatcher->current_view, event); | ||||
| @ -219,13 +257,22 @@ void view_dispatcher_handle_input(ViewDispatcher* view_dispatcher, InputEvent* e | ||||
|                         return; | ||||
|                     } | ||||
|                 } | ||||
|         } else if(event->key == InputKeyOk) { | ||||
|             view_id = view_next(view_dispatcher->current_view); | ||||
|             } | ||||
|             if(!is_consumed) { | ||||
|                 view_dispatcher_switch_to_view(view_dispatcher, view_id); | ||||
|             } | ||||
|         } | ||||
|     } else if(view_dispatcher->ongoing_input_view && event->type == InputTypeRelease) { | ||||
|         FURI_LOG_W( | ||||
|             "ViewDispatcher", | ||||
|             "View changed while key press %p -> %p. Sending key: %s, type: %s, sequence: %p to previous view port", | ||||
|             view_dispatcher->ongoing_input_view, | ||||
|             view_dispatcher->current_view, | ||||
|             input_get_key_name(event->key), | ||||
|             input_get_type_name(event->type), | ||||
|             event->sequence); | ||||
|         view_input(view_dispatcher->ongoing_input_view, event); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void view_dispatcher_handle_tick_event(ViewDispatcher* view_dispatcher) { | ||||
|  | ||||
| @ -113,6 +113,7 @@ void view_dispatcher_remove_view(ViewDispatcher* view_dispatcher, uint32_t view_ | ||||
| /** Switch to View
 | ||||
|  * @param view_dispatcher ViewDispatcher instance | ||||
|  * @param view_id View id to register | ||||
|  * @warning switching may be delayed till input events complementarity reached | ||||
|  */ | ||||
| void view_dispatcher_switch_to_view(ViewDispatcher* view_dispatcher, uint32_t view_id); | ||||
| 
 | ||||
|  | ||||
| @ -14,7 +14,12 @@ struct ViewDispatcher { | ||||
|     Gui* gui; | ||||
|     ViewPort* view_port; | ||||
|     ViewDict_t views; | ||||
| 
 | ||||
|     View* current_view; | ||||
| 
 | ||||
|     View* ongoing_input_view; | ||||
|     uint8_t ongoing_input; | ||||
| 
 | ||||
|     ViewDispatcherCustomEventCallback custom_event_callback; | ||||
|     ViewDispatcherNavigationEventCallback navigation_event_callback; | ||||
|     ViewDispatcherTickEventCallback tick_event_callback; | ||||
|  | ||||
| @ -15,7 +15,6 @@ struct View { | ||||
| 
 | ||||
|     ViewModelType model_type; | ||||
|     ViewNavigationCallback previous_callback; | ||||
|     ViewNavigationCallback next_callback; | ||||
|     ViewCallback enter_callback; | ||||
|     ViewCallback exit_callback; | ||||
|     ViewOrientation orientation; | ||||
| @ -42,9 +41,6 @@ bool view_custom(View* view, uint32_t event); | ||||
| /* Previous Callback for View dispatcher */ | ||||
| uint32_t view_previous(View* view); | ||||
| 
 | ||||
| /* Next Callback for View dispatcher */ | ||||
| uint32_t view_next(View* view); | ||||
| 
 | ||||
| /* Enter Callback for View dispatcher */ | ||||
| void view_enter(View* view); | ||||
| 
 | ||||
|  | ||||
| @ -7,6 +7,33 @@ | ||||
| 
 | ||||
| // TODO add mutex to view_port ops
 | ||||
| 
 | ||||
| static void view_port_rotate_buttons(InputEvent* event) { | ||||
|     switch(event->key) { | ||||
|     case InputKeyUp: | ||||
|         event->key = InputKeyRight; | ||||
|         break; | ||||
|     case InputKeyDown: | ||||
|         event->key = InputKeyLeft; | ||||
|         break; | ||||
|     case InputKeyRight: | ||||
|         event->key = InputKeyDown; | ||||
|         break; | ||||
|     case InputKeyLeft: | ||||
|         event->key = InputKeyUp; | ||||
|         break; | ||||
|     default: | ||||
|         break; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| static void view_port_setup_canvas_orientation(const ViewPort* view_port, Canvas* canvas) { | ||||
|     if(view_port->orientation == ViewPortOrientationHorizontal) { | ||||
|         canvas_set_orientation(canvas, CanvasOrientationHorizontal); | ||||
|     } else if(view_port->orientation == ViewPortOrientationVertical) { | ||||
|         canvas_set_orientation(canvas, CanvasOrientationVertical); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| ViewPort* view_port_alloc() { | ||||
|     ViewPort* view_port = furi_alloc(sizeof(ViewPort)); | ||||
|     view_port->orientation = ViewPortOrientationHorizontal; | ||||
| @ -84,6 +111,7 @@ void view_port_draw(ViewPort* view_port, Canvas* canvas) { | ||||
|     furi_check(view_port->gui); | ||||
| 
 | ||||
|     if(view_port->draw_callback) { | ||||
|         view_port_setup_canvas_orientation(view_port, canvas); | ||||
|         view_port->draw_callback(canvas, view_port->draw_callback_context); | ||||
|     } | ||||
| } | ||||
| @ -94,6 +122,9 @@ void view_port_input(ViewPort* view_port, InputEvent* event) { | ||||
|     furi_check(view_port->gui); | ||||
| 
 | ||||
|     if(view_port->input_callback) { | ||||
|         if(view_port_get_orientation(view_port) == ViewPortOrientationVertical) { | ||||
|             view_port_rotate_buttons(event); | ||||
|         } | ||||
|         view_port->input_callback(event, view_port->input_callback_context); | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -10,18 +10,20 @@ | ||||
| #include <memory> | ||||
| 
 | ||||
| void ibutton_cli(Cli* cli, string_t args, void* context); | ||||
| void onewire_cli(Cli* cli, string_t args, void* context); | ||||
| 
 | ||||
| // app cli function
 | ||||
| extern "C" void ibutton_cli_init() { | ||||
|     Cli* cli = static_cast<Cli*>(furi_record_open("cli")); | ||||
|     cli_add_command(cli, "tm", CliCommandFlagDefault, ibutton_cli, cli); | ||||
|     cli_add_command(cli, "ikey", CliCommandFlagDefault, ibutton_cli, cli); | ||||
|     cli_add_command(cli, "onewire", CliCommandFlagDefault, onewire_cli, cli); | ||||
|     furi_record_close("cli"); | ||||
| } | ||||
| 
 | ||||
| void ibutton_cli_print_usage() { | ||||
|     printf("Usage:\r\n"); | ||||
|     printf("tm read\r\n"); | ||||
|     printf("tm <write | emulate> <key_type> <key_data>\r\n"); | ||||
|     printf("ikey read\r\n"); | ||||
|     printf("ikey <write | emulate> <key_type> <key_data>\r\n"); | ||||
|     printf("\t<key_type> choose from:\r\n"); | ||||
|     printf("\tDallas (8 bytes key_data)\r\n"); | ||||
|     printf("\tCyfral (2 bytes key_data)\r\n"); | ||||
| @ -232,3 +234,51 @@ void ibutton_cli(Cli* cli, string_t args, void* context) { | ||||
| 
 | ||||
|     string_clear(cmd); | ||||
| } | ||||
| 
 | ||||
| void onewire_cli_print_usage() { | ||||
|     printf("Usage:\r\n"); | ||||
|     printf("onewire search\r\n"); | ||||
| }; | ||||
| 
 | ||||
| void onewire_cli_search(Cli* cli) { | ||||
|     OneWireMaster onewire(&ibutton_gpio); | ||||
|     uint8_t address[8]; | ||||
|     bool done = false; | ||||
| 
 | ||||
|     printf("Search started\r\n"); | ||||
| 
 | ||||
|     onewire.start(); | ||||
|     while(!done) { | ||||
|         if(onewire.search(address, true) != 1) { | ||||
|             printf("Search finished\r\n"); | ||||
|             onewire.reset_search(); | ||||
|             done = true; | ||||
|             return; | ||||
|         } else { | ||||
|             printf("Found: "); | ||||
|             for(uint8_t i = 0; i < 8; i++) { | ||||
|                 printf("%02X", address[i]); | ||||
|             } | ||||
|             printf("\r\n"); | ||||
|         } | ||||
|         delay(100); | ||||
|     } | ||||
|     onewire.stop(); | ||||
| } | ||||
| 
 | ||||
| void onewire_cli(Cli* cli, string_t args, void* context) { | ||||
|     string_t cmd; | ||||
|     string_init(cmd); | ||||
| 
 | ||||
|     if(!args_read_string_and_trim(args, cmd)) { | ||||
|         string_clear(cmd); | ||||
|         onewire_cli_print_usage(); | ||||
|         return; | ||||
|     } | ||||
| 
 | ||||
|     if(string_cmp_str(cmd, "search") == 0) { | ||||
|         onewire_cli_search(cli); | ||||
|     } | ||||
| 
 | ||||
|     string_clear(cmd); | ||||
| } | ||||
| @ -23,6 +23,7 @@ inline static void input_timer_stop(osTimerId_t timer_id) { | ||||
| void input_press_timer_callback(void* arg) { | ||||
|     InputPinState* input_pin = arg; | ||||
|     InputEvent event; | ||||
|     event.sequence = input_pin->counter; | ||||
|     event.key = input_pin->pin->key; | ||||
|     input_pin->press_counter++; | ||||
|     if(input_pin->press_counter == INPUT_LONG_PRESS_COUNTS) { | ||||
| @ -91,6 +92,31 @@ void input_cli_send(Cli* cli, string_t args, void* context) { | ||||
|     notify_pubsub(&input->event_pubsub, &event); | ||||
| } | ||||
| 
 | ||||
| const char* input_get_key_name(InputKey key) { | ||||
|     for(size_t i = 0; i < input_pins_count; i++) { | ||||
|         if(input_pins[i].key == key) { | ||||
|             return input_pins[i].name; | ||||
|         } | ||||
|     } | ||||
|     return "Unknown"; | ||||
| } | ||||
| 
 | ||||
| const char* input_get_type_name(InputType type) { | ||||
|     switch(type) { | ||||
|     case InputTypePress: | ||||
|         return "Press"; | ||||
|     case InputTypeRelease: | ||||
|         return "Release"; | ||||
|     case InputTypeShort: | ||||
|         return "Short"; | ||||
|     case InputTypeLong: | ||||
|         return "Long"; | ||||
|     case InputTypeRepeat: | ||||
|         return "Repeat"; | ||||
|     } | ||||
|     return "Unknown"; | ||||
| } | ||||
| 
 | ||||
| int32_t input_srv() { | ||||
|     input = furi_alloc(sizeof(Input)); | ||||
|     input->thread = osThreadGetId(); | ||||
| @ -103,10 +129,9 @@ int32_t input_srv() { | ||||
|             input->cli, "input_send", CliCommandFlagParallelSafe, input_cli_send, input); | ||||
|     } | ||||
| 
 | ||||
|     const size_t pin_count = input_pins_count; | ||||
|     input->pin_states = furi_alloc(pin_count * sizeof(InputPinState)); | ||||
|     input->pin_states = furi_alloc(input_pins_count * sizeof(InputPinState)); | ||||
| 
 | ||||
|     for(size_t i = 0; i < pin_count; i++) { | ||||
|     for(size_t i = 0; i < input_pins_count; i++) { | ||||
|         GpioPin gpio = {(GPIO_TypeDef*)input_pins[i].port, (uint16_t)input_pins[i].pin}; | ||||
|         hal_gpio_add_int_callback(&gpio, input_isr, NULL); | ||||
|         input->pin_states[i].pin = &input_pins[i]; | ||||
| @ -119,7 +144,7 @@ int32_t input_srv() { | ||||
| 
 | ||||
|     while(1) { | ||||
|         bool is_changing = false; | ||||
|         for(size_t i = 0; i < pin_count; i++) { | ||||
|         for(size_t i = 0; i < input_pins_count; i++) { | ||||
|             bool state = GPIO_Read(input->pin_states[i]); | ||||
|             if(input->pin_states[i].debounce > 0 && | ||||
|                input->pin_states[i].debounce < INPUT_DEBOUNCE_TICKS) { | ||||
| @ -131,14 +156,15 @@ int32_t input_srv() { | ||||
|                 // Common state info
 | ||||
|                 InputEvent event; | ||||
|                 event.key = input->pin_states[i].pin->key; | ||||
|                 event.type = input->pin_states[i].state ? InputTypePress : InputTypeRelease; | ||||
|                 // Send Press/Release event
 | ||||
|                 notify_pubsub(&input->event_pubsub, &event); | ||||
| 
 | ||||
|                 // Short / Long / Repeat timer routine
 | ||||
|                 if(state) { | ||||
|                     input->counter++; | ||||
|                     input->pin_states[i].counter = input->counter; | ||||
|                     event.sequence = input->pin_states[i].counter; | ||||
|                     input_timer_start(input->pin_states[i].press_timer, INPUT_PRESS_TICKS); | ||||
|                 } else { | ||||
|                     event.sequence = input->pin_states[i].counter; | ||||
|                     input_timer_stop(input->pin_states[i].press_timer); | ||||
|                     if(input->pin_states[i].press_counter < INPUT_LONG_PRESS_COUNTS) { | ||||
|                         event.type = InputTypeShort; | ||||
| @ -146,6 +172,10 @@ int32_t input_srv() { | ||||
|                     } | ||||
|                     input->pin_states[i].press_counter = 0; | ||||
|                 } | ||||
| 
 | ||||
|                 // Send Press/Release event
 | ||||
|                 event.type = input->pin_states[i].state ? InputTypePress : InputTypeRelease; | ||||
|                 notify_pubsub(&input->event_pubsub, &event); | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|  | ||||
| @ -15,6 +15,19 @@ typedef enum { | ||||
| 
 | ||||
| /* Input Event, dispatches with PubSub */ | ||||
| typedef struct { | ||||
|     uint32_t sequence; | ||||
|     InputKey key; | ||||
|     InputType type; | ||||
| } InputEvent; | ||||
| 
 | ||||
| /** Get human readable input key name
 | ||||
|  * @param key - InputKey | ||||
|  * @return string | ||||
|  */ | ||||
| const char* input_get_key_name(InputKey key); | ||||
| 
 | ||||
| /** Get human readable input type name
 | ||||
|  * @param type - InputType | ||||
|  * @return string | ||||
|  */ | ||||
| const char* input_get_type_name(InputType type); | ||||
|  | ||||
| @ -24,6 +24,7 @@ typedef struct { | ||||
|     volatile uint8_t debounce; | ||||
|     volatile osTimerId_t press_timer; | ||||
|     volatile uint8_t press_counter; | ||||
|     volatile uint32_t counter; | ||||
| } InputPinState; | ||||
| 
 | ||||
| /* Input state */ | ||||
| @ -32,6 +33,7 @@ typedef struct { | ||||
|     PubSub event_pubsub; | ||||
|     InputPinState* pin_states; | ||||
|     Cli* cli; | ||||
|     volatile uint32_t counter; | ||||
| } Input; | ||||
| 
 | ||||
| /* Input press timer callback */ | ||||
|  | ||||
| @ -383,11 +383,6 @@ int32_t music_player_app(void* p) { | ||||
|     Gui* gui = furi_record_open("gui"); | ||||
|     gui_add_view_port(gui, view_port, GuiLayerFullscreen); | ||||
| 
 | ||||
|     // open input record
 | ||||
|     PubSub* input_events_record = furi_record_open("input_events"); | ||||
|     // prepare "do nothing" event
 | ||||
|     InputEvent input_event = {InputKeyRight, true}; | ||||
| 
 | ||||
|     // start player thread
 | ||||
|     // TODO change to fuirac_start
 | ||||
|     osThreadAttr_t player_attr = {.name = "music_player_thread", .stack_size = 512}; | ||||
| @ -410,14 +405,8 @@ int32_t music_player_app(void* p) { | ||||
|                 // press events
 | ||||
|                 if(event.value.input.type == InputTypeShort && | ||||
|                    event.value.input.key == InputKeyBack) { | ||||
|                     osThreadTerminate(player); | ||||
|                     hal_pwm_stop(&SPEAKER_TIM, SPEAKER_CH); | ||||
|                     view_port_enabled_set(view_port, false); | ||||
|                     gui_remove_view_port(gui, view_port); | ||||
|                     view_port_free(view_port); | ||||
|                     osMessageQueueDelete(event_queue); | ||||
| 
 | ||||
|                     return 0; | ||||
|                     release_mutex(&state_mutex, state); | ||||
|                     break; | ||||
|                 } | ||||
| 
 | ||||
|                 if(event.value.input.type == InputTypePress && | ||||
| @ -442,9 +431,6 @@ int32_t music_player_app(void* p) { | ||||
|                 } | ||||
| 
 | ||||
|             } else if(event.type == EventTypeNote) { | ||||
|                 // send "do nothing" event to prevent display backlight off
 | ||||
|                 notify_pubsub(input_events_record, &input_event); | ||||
| 
 | ||||
|                 state->note_record = event.value.note_record; | ||||
| 
 | ||||
|                 for(size_t i = note_stack_size - 1; i > 0; i--) { | ||||
| @ -460,5 +446,14 @@ int32_t music_player_app(void* p) { | ||||
|         release_mutex(&state_mutex, state); | ||||
|     } | ||||
| 
 | ||||
|     osThreadTerminate(player); | ||||
|     hal_pwm_stop(&SPEAKER_TIM, SPEAKER_CH); | ||||
|     view_port_enabled_set(view_port, false); | ||||
|     gui_remove_view_port(gui, view_port); | ||||
|     furi_record_close("gui"); | ||||
|     view_port_free(view_port); | ||||
|     osMessageQueueDelete(event_queue); | ||||
|     delete_mutex(&state_mutex); | ||||
| 
 | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
							
								
								
									
										49
									
								
								applications/nfc/helpers/nfc_emv_parser.c
									
									
									
									
									
										Executable file
									
								
							
							
						
						| @ -0,0 +1,49 @@ | ||||
| #include "nfc_emv_parser.h" | ||||
| 
 | ||||
| #include <file-worker.h> | ||||
| 
 | ||||
| static bool | ||||
|     nfc_emv_parser_get_value(const char* file_path, string_t key, char delimiter, string_t value) { | ||||
|     bool found = false; | ||||
|     FileWorker* file_worker = file_worker_alloc(true); | ||||
| 
 | ||||
|     if(file_worker_open(file_worker, file_path, FSAM_READ, FSOM_OPEN_EXISTING)) { | ||||
|         if(file_worker_get_value_from_key(file_worker, key, delimiter, value)) { | ||||
|             found = true; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     file_worker_close(file_worker); | ||||
|     file_worker_free(file_worker); | ||||
|     return found; | ||||
| } | ||||
| 
 | ||||
| bool nfc_emv_parser_get_aid_name(uint8_t* aid, uint8_t aid_len, string_t aid_name) { | ||||
|     bool result = false; | ||||
|     string_t key; | ||||
|     string_init(key); | ||||
|     for(uint8_t i = 0; i < aid_len; i++) { | ||||
|         string_cat_printf(key, "%02X", aid[i]); | ||||
|     } | ||||
|     result = nfc_emv_parser_get_value("/ext/nfc/emv/aid.nfc", key, ' ', aid_name); | ||||
|     string_clear(key); | ||||
|     return result; | ||||
| } | ||||
| 
 | ||||
| bool nfc_emv_parser_get_country_name(uint16_t country_code, string_t country_name) { | ||||
|     bool result = false; | ||||
|     string_t key; | ||||
|     string_init_printf(key, "%04X", country_code); | ||||
|     result = nfc_emv_parser_get_value("/ext/nfc/emv/country_code.nfc", key, ' ', country_name); | ||||
|     string_clear(key); | ||||
|     return result; | ||||
| } | ||||
| 
 | ||||
| bool nfc_emv_parser_get_currency_name(uint16_t currency_code, string_t currency_name) { | ||||
|     bool result = false; | ||||
|     string_t key; | ||||
|     string_init_printf(key, "%04X", currency_code); | ||||
|     result = nfc_emv_parser_get_value("/ext/nfc/emv/currency_code.nfc", key, ' ', currency_name); | ||||
|     string_clear(key); | ||||
|     return result; | ||||
| } | ||||
							
								
								
									
										27
									
								
								applications/nfc/helpers/nfc_emv_parser.h
									
									
									
									
									
										Executable file
									
								
							
							
						
						| @ -0,0 +1,27 @@ | ||||
| #pragma once | ||||
| 
 | ||||
| #include <stdint.h> | ||||
| #include <stdbool.h> | ||||
| #include <m-string.h> | ||||
| 
 | ||||
| /** Get EMV application name by number
 | ||||
|  * @param aid - AID number array | ||||
|  * @param aid_len - AID length | ||||
|  * @param aid_name - string to keep AID name | ||||
|  * @return - true if AID found, false otherwies | ||||
|  */ | ||||
| bool nfc_emv_parser_get_aid_name(uint8_t* aid, uint8_t aid_len, string_t aid_name); | ||||
| 
 | ||||
| /** Get country name by country code
 | ||||
|  * @param country_code - ISO 3166 country code | ||||
|  * @param country_name - string to keep country name | ||||
|  * @return - true if country found, false otherwies | ||||
|  */ | ||||
| bool nfc_emv_parser_get_country_name(uint16_t country_code, string_t country_name); | ||||
| 
 | ||||
| /** Get currency name by currency code
 | ||||
|  * @param currency_code - ISO 3166 currency code | ||||
|  * @param currency_name - string to keep currency name | ||||
|  * @return - true if currency found, false otherwies | ||||
|  */ | ||||
| bool nfc_emv_parser_get_currency_name(uint16_t currency_code, string_t currency_name); | ||||
| @ -54,7 +54,7 @@ void nfc_cli_emulate(Cli* cli, string_t args, void* context) { | ||||
|     printf("Emulating NFC-A Type: T2T UID: CF72D440 SAK: 20 ATQA: 00/04\r\n"); | ||||
|     printf("Press Ctrl+C to abort\r\n"); | ||||
| 
 | ||||
|     NfcDeviceCommomData params = { | ||||
|     NfcDeviceCommonData params = { | ||||
|         .uid = {0x36, 0x9C, 0xe7, 0xb1, 0x0A, 0xC1, 0x34}, | ||||
|         .uid_len = 7, | ||||
|         .atqa = {0x44, 0x00}, | ||||
|  | ||||
| @ -10,7 +10,7 @@ static const char* nfc_app_folder = "/any/nfc"; | ||||
| static const char* nfc_app_extension = ".nfc"; | ||||
| static const char* nfc_app_shadow_extension = ".shd"; | ||||
| 
 | ||||
| static bool nfc_device_read_hex(string_t str, uint8_t* buff, uint16_t len) { | ||||
| static bool nfc_device_read_hex(string_t str, uint8_t* buff, uint16_t len, uint8_t delim_len) { | ||||
|     string_strim(str); | ||||
|     uint8_t nibble_high = 0; | ||||
|     uint8_t nibble_low = 0; | ||||
| @ -20,7 +20,7 @@ static bool nfc_device_read_hex(string_t str, uint8_t* buff, uint16_t len) { | ||||
|         if(hex_char_to_hex_nibble(string_get_char(str, 0), &nibble_high) && | ||||
|            hex_char_to_hex_nibble(string_get_char(str, 1), &nibble_low)) { | ||||
|             buff[i] = (nibble_high << 4) | nibble_low; | ||||
|             string_right(str, 3); | ||||
|             string_right(str, delim_len + 2); | ||||
|         } else { | ||||
|             parsed = false; | ||||
|             break; | ||||
| @ -60,7 +60,7 @@ bool nfc_device_parse_format_string(NfcDevice* dev, string_t format_string) { | ||||
| } | ||||
| 
 | ||||
| uint16_t nfc_device_prepare_uid_string(NfcDevice* dev, string_t uid_string) { | ||||
|     NfcDeviceCommomData* uid_data = &dev->dev_data.nfc_data; | ||||
|     NfcDeviceCommonData* uid_data = &dev->dev_data.nfc_data; | ||||
|     string_printf(uid_string, "UID len: %02X UID: ", dev->dev_data.nfc_data.uid_len); | ||||
|     for(uint8_t i = 0; i < uid_data->uid_len; i++) { | ||||
|         string_cat_printf(uid_string, "%02X ", uid_data->uid[i]); | ||||
| @ -75,28 +75,28 @@ uint16_t nfc_device_prepare_uid_string(NfcDevice* dev, string_t uid_string) { | ||||
| } | ||||
| 
 | ||||
| bool nfc_device_parse_uid_string(NfcDevice* dev, string_t uid_string) { | ||||
|     NfcDeviceCommomData* uid_data = &dev->dev_data.nfc_data; | ||||
|     NfcDeviceCommonData* uid_data = &dev->dev_data.nfc_data; | ||||
|     bool parsed = false; | ||||
| 
 | ||||
|     do { | ||||
|         // strlen("UID len: ") = 9
 | ||||
|         string_right(uid_string, 9); | ||||
|         if(!nfc_device_read_hex(uid_string, &uid_data->uid_len, 1)) { | ||||
|         if(!nfc_device_read_hex(uid_string, &uid_data->uid_len, 1, 1)) { | ||||
|             break; | ||||
|         } | ||||
|         // strlen("UID: ") = 5
 | ||||
|         string_right(uid_string, 5); | ||||
|         if(!nfc_device_read_hex(uid_string, uid_data->uid, uid_data->uid_len)) { | ||||
|         if(!nfc_device_read_hex(uid_string, uid_data->uid, uid_data->uid_len, 1)) { | ||||
|             break; | ||||
|         } | ||||
|         // strlen("ATQA: ") = 6
 | ||||
|         string_right(uid_string, 6); | ||||
|         if(!nfc_device_read_hex(uid_string, uid_data->atqa, 2)) { | ||||
|         if(!nfc_device_read_hex(uid_string, uid_data->atqa, 2, 1)) { | ||||
|             break; | ||||
|         } | ||||
|         // strlen("SAK: ") = 5
 | ||||
|         string_right(uid_string, 5); | ||||
|         if(!nfc_device_read_hex(uid_string, &uid_data->sak, 1)) { | ||||
|         if(!nfc_device_read_hex(uid_string, &uid_data->sak, 1, 1)) { | ||||
|             break; | ||||
|         } | ||||
|         parsed = true; | ||||
| @ -149,13 +149,13 @@ bool nfc_device_parse_mifare_ul_string(NfcDevice* dev, string_t mifare_ul_string | ||||
|     do { | ||||
|         // strlen("Signature: ") = 11
 | ||||
|         string_right(mifare_ul_string, 11); | ||||
|         if(!nfc_device_read_hex(mifare_ul_string, data->signature, sizeof(data->signature))) { | ||||
|         if(!nfc_device_read_hex(mifare_ul_string, data->signature, sizeof(data->signature), 1)) { | ||||
|             break; | ||||
|         } | ||||
|         // strlen("Version: ") = 9
 | ||||
|         string_right(mifare_ul_string, 9); | ||||
|         if(!nfc_device_read_hex( | ||||
|                mifare_ul_string, (uint8_t*)&data->version, sizeof(data->version))) { | ||||
|                mifare_ul_string, (uint8_t*)&data->version, sizeof(data->version), 1)) { | ||||
|             break; | ||||
|         } | ||||
|         string_strim(mifare_ul_string); | ||||
| @ -184,7 +184,7 @@ bool nfc_device_parse_mifare_ul_string(NfcDevice* dev, string_t mifare_ul_string | ||||
|         string_right(mifare_ul_string, ws + 1); | ||||
|         // Read data
 | ||||
|         for(uint16_t i = 0; i < data->data_size; i += 4) { | ||||
|             if(!nfc_device_read_hex(mifare_ul_string, &data->data[i], 4)) { | ||||
|             if(!nfc_device_read_hex(mifare_ul_string, &data->data[i], 4, 1)) { | ||||
|                 break; | ||||
|             } | ||||
|         } | ||||
| @ -208,6 +208,12 @@ uint16_t nfc_device_prepare_bank_card_string(NfcDevice* dev, string_t bank_card_ | ||||
|         string_cat_printf( | ||||
|             bank_card_string, "\nExp date: %02X/%02X", data->exp_mon, data->exp_year); | ||||
|     } | ||||
|     if(data->country_code) { | ||||
|         string_cat_printf(bank_card_string, "\nCountry code: %04X", data->country_code); | ||||
|     } | ||||
|     if(data->currency_code) { | ||||
|         string_cat_printf(bank_card_string, "\nCurrency code: %04X", data->currency_code); | ||||
|     } | ||||
|     return string_size(bank_card_string); | ||||
| } | ||||
| 
 | ||||
| @ -215,6 +221,7 @@ bool nfc_device_parse_bank_card_string(NfcDevice* dev, string_t bank_card_string | ||||
|     NfcEmvData* data = &dev->dev_data.emv_data; | ||||
|     bool parsed = false; | ||||
|     int res = 0; | ||||
|     uint8_t code[2] = {}; | ||||
|     memset(data, 0, sizeof(NfcEmvData)); | ||||
| 
 | ||||
|     do { | ||||
| @ -226,7 +233,7 @@ bool nfc_device_parse_bank_card_string(NfcDevice* dev, string_t bank_card_string | ||||
|         string_right(bank_card_string, 9); | ||||
|         size_t ws = string_search_char(bank_card_string, ':'); | ||||
|         string_right(bank_card_string, ws + 1); | ||||
|         if(!nfc_device_read_hex(bank_card_string, data->aid, data->aid_len)) { | ||||
|         if(!nfc_device_read_hex(bank_card_string, data->aid, data->aid_len, 1)) { | ||||
|             break; | ||||
|         } | ||||
|         res = sscanf(string_get_cstr(bank_card_string), "Name: %s\n", data->name); | ||||
| @ -237,7 +244,7 @@ bool nfc_device_parse_bank_card_string(NfcDevice* dev, string_t bank_card_string | ||||
|         string_right(bank_card_string, ws + 1); | ||||
|         // strlen("Number: ") = 8
 | ||||
|         string_right(bank_card_string, 8); | ||||
|         if(!nfc_device_read_hex(bank_card_string, data->number, sizeof(data->number))) { | ||||
|         if(!nfc_device_read_hex(bank_card_string, data->number, sizeof(data->number), 1)) { | ||||
|             break; | ||||
|         } | ||||
|         parsed = true; | ||||
| @ -246,8 +253,24 @@ bool nfc_device_parse_bank_card_string(NfcDevice* dev, string_t bank_card_string | ||||
|         if(ws != STRING_FAILURE) { | ||||
|             // strlen("Exp date: ") = 10
 | ||||
|             string_right(bank_card_string, 10); | ||||
|             nfc_device_read_hex(bank_card_string, &data->exp_mon, 1); | ||||
|             nfc_device_read_hex(bank_card_string, &data->exp_year, 1); | ||||
|             nfc_device_read_hex(bank_card_string, &data->exp_mon, 1, 1); | ||||
|             nfc_device_read_hex(bank_card_string, &data->exp_year, 1, 1); | ||||
|         } | ||||
|         // Check country code presence
 | ||||
|         ws = string_search_str(bank_card_string, "Country code: "); | ||||
|         if(ws != STRING_FAILURE) { | ||||
|             // strlen("Country code: ") = 14
 | ||||
|             string_right(bank_card_string, 14); | ||||
|             nfc_device_read_hex(bank_card_string, code, 2, 0); | ||||
|             data->country_code = code[0] << 8 | code[1]; | ||||
|         } | ||||
|         // Check currency code presence
 | ||||
|         ws = string_search_str(bank_card_string, "Currency code: "); | ||||
|         if(ws != STRING_FAILURE) { | ||||
|             // strlen("Currency code: ") = 15
 | ||||
|             string_right(bank_card_string, 15); | ||||
|             nfc_device_read_hex(bank_card_string, code, 2, 0); | ||||
|             data->currency_code = code[0] << 8 | code[1]; | ||||
|         } | ||||
|     } while(0); | ||||
| 
 | ||||
|  | ||||
| @ -34,7 +34,7 @@ typedef struct { | ||||
|     uint8_t sak; | ||||
|     NfcDeviceType device; | ||||
|     NfcProtocol protocol; | ||||
| } NfcDeviceCommomData; | ||||
| } NfcDeviceCommonData; | ||||
| 
 | ||||
| typedef struct { | ||||
|     char name[32]; | ||||
| @ -43,11 +43,12 @@ typedef struct { | ||||
|     uint8_t number[8]; | ||||
|     uint8_t exp_mon; | ||||
|     uint8_t exp_year; | ||||
|     char cardholder[32]; | ||||
|     uint16_t country_code; | ||||
|     uint16_t currency_code; | ||||
| } NfcEmvData; | ||||
| 
 | ||||
| typedef struct { | ||||
|     NfcDeviceCommomData nfc_data; | ||||
|     NfcDeviceCommonData nfc_data; | ||||
|     union { | ||||
|         NfcEmvData emv_data; | ||||
|         MifareUlData mf_ul_data; | ||||
|  | ||||
| @ -100,7 +100,7 @@ void nfc_worker_detect(NfcWorker* nfc_worker) { | ||||
|     rfalNfcDevice* dev_list; | ||||
|     rfalNfcDevice* dev; | ||||
|     uint8_t dev_cnt; | ||||
|     NfcDeviceCommomData* result = &nfc_worker->dev_data->nfc_data; | ||||
|     NfcDeviceCommonData* result = &nfc_worker->dev_data->nfc_data; | ||||
| 
 | ||||
|     while(nfc_worker->state == NfcWorkerStateDetect) { | ||||
|         if(furi_hal_nfc_detect(&dev_list, &dev_cnt, 1000, true)) { | ||||
| @ -141,7 +141,7 @@ void nfc_worker_detect(NfcWorker* nfc_worker) { | ||||
| } | ||||
| 
 | ||||
| void nfc_worker_emulate(NfcWorker* nfc_worker) { | ||||
|     NfcDeviceCommomData* data = &nfc_worker->dev_data->nfc_data; | ||||
|     NfcDeviceCommonData* data = &nfc_worker->dev_data->nfc_data; | ||||
|     while(nfc_worker->state == NfcWorkerStateEmulate) { | ||||
|         if(furi_hal_nfc_listen(data->uid, data->uid_len, data->atqa, data->sak, false, 100)) { | ||||
|             FURI_LOG_I(NFC_WORKER_TAG, "Reader detected"); | ||||
| @ -328,6 +328,12 @@ void nfc_worker_read_emv(NfcWorker* nfc_worker) { | ||||
|                             result->emv_data.exp_mon = emv_app.exp_month; | ||||
|                             result->emv_data.exp_year = emv_app.exp_year; | ||||
|                         } | ||||
|                         if(emv_app.country_code) { | ||||
|                             result->emv_data.country_code = emv_app.country_code; | ||||
|                         } | ||||
|                         if(emv_app.currency_code) { | ||||
|                             result->emv_data.currency_code = emv_app.currency_code; | ||||
|                         } | ||||
|                         // Notify caller and exit
 | ||||
|                         if(nfc_worker->callback) { | ||||
|                             nfc_worker->callback(nfc_worker->context); | ||||
| @ -358,7 +364,7 @@ void nfc_worker_emulate_apdu(NfcWorker* nfc_worker) { | ||||
|     uint16_t tx_len = 0; | ||||
|     uint8_t* rx_buff; | ||||
|     uint16_t* rx_len; | ||||
|     NfcDeviceCommomData params = { | ||||
|     NfcDeviceCommonData params = { | ||||
|         .uid = {0xCF, 0x72, 0xd4, 0x40}, | ||||
|         .uid_len = 4, | ||||
|         .atqa = {0x00, 0x04}, | ||||
|  | ||||
| @ -18,7 +18,7 @@ void nfc_scene_delete_on_enter(void* context) { | ||||
|     widget_add_button_element( | ||||
|         nfc->widget, GuiButtonTypeRight, "Delete", nfc_scene_delete_widget_callback, nfc); | ||||
|     char uid_str[32]; | ||||
|     NfcDeviceCommomData* data = &nfc->dev.dev_data.nfc_data; | ||||
|     NfcDeviceCommonData* data = &nfc->dev.dev_data.nfc_data; | ||||
|     if(data->uid_len == 4) { | ||||
|         snprintf( | ||||
|             uid_str, | ||||
|  | ||||
							
								
								
									
										33
									
								
								applications/nfc/scenes/nfc_scene_device_info.c
									
									
									
									
									
										
										
										Normal file → Executable file
									
								
							
							
						
						| @ -1,6 +1,6 @@ | ||||
| #include "../nfc_i.h" | ||||
| 
 | ||||
| #define NFC_SCENE_DEVICE_INFO_TEXTBOX_CUSTOM_EVENT (0UL) | ||||
| #define NFC_SCENE_DEVICE_INFO_BACK_EVENT (0UL) | ||||
| 
 | ||||
| enum { | ||||
|     NfcSceneDeviceInfoUid, | ||||
| @ -8,26 +8,27 @@ enum { | ||||
| }; | ||||
| 
 | ||||
| void nfc_scene_device_info_widget_callback(GuiButtonType result, void* context) { | ||||
|     Nfc* nfc = (Nfc*)context; | ||||
| 
 | ||||
|     Nfc* nfc = context; | ||||
|     view_dispatcher_send_custom_event(nfc->view_dispatcher, result); | ||||
| } | ||||
| 
 | ||||
| void nfc_scene_device_info_dialog_callback(DialogExResult result, void* context) { | ||||
|     Nfc* nfc = (Nfc*)context; | ||||
| 
 | ||||
|     Nfc* nfc = context; | ||||
|     view_dispatcher_send_custom_event(nfc->view_dispatcher, result); | ||||
| } | ||||
| 
 | ||||
| void nfc_scene_device_info_text_box_callback(void* context) { | ||||
|     Nfc* nfc = (Nfc*)context; | ||||
|     Nfc* nfc = context; | ||||
|     view_dispatcher_send_custom_event(nfc->view_dispatcher, NFC_SCENE_DEVICE_INFO_BACK_EVENT); | ||||
| } | ||||
| 
 | ||||
|     view_dispatcher_send_custom_event( | ||||
|         nfc->view_dispatcher, NFC_SCENE_DEVICE_INFO_TEXTBOX_CUSTOM_EVENT); | ||||
| void nfc_scene_device_info_bank_card_callback(GuiButtonType result, void* context) { | ||||
|     Nfc* nfc = context; | ||||
|     view_dispatcher_send_custom_event(nfc->view_dispatcher, NFC_SCENE_DEVICE_INFO_BACK_EVENT); | ||||
| } | ||||
| 
 | ||||
| void nfc_scene_device_info_on_enter(void* context) { | ||||
|     Nfc* nfc = (Nfc*)context; | ||||
|     Nfc* nfc = context; | ||||
| 
 | ||||
|     // Setup Custom Widget view
 | ||||
|     widget_add_string_element( | ||||
| @ -37,7 +38,7 @@ void nfc_scene_device_info_on_enter(void* context) { | ||||
|     widget_add_button_element( | ||||
|         nfc->widget, GuiButtonTypeRight, "Data", nfc_scene_device_info_widget_callback, nfc); | ||||
|     char uid_str[32]; | ||||
|     NfcDeviceCommomData* data = &nfc->dev.dev_data.nfc_data; | ||||
|     NfcDeviceCommonData* data = &nfc->dev.dev_data.nfc_data; | ||||
|     if(data->uid_len == 4) { | ||||
|         snprintf( | ||||
|             uid_str, | ||||
| @ -107,19 +108,23 @@ void nfc_scene_device_info_on_enter(void* context) { | ||||
|         BankCard* bank_card = nfc->bank_card; | ||||
|         bank_card_set_name(bank_card, emv_data->name); | ||||
|         bank_card_set_number(bank_card, emv_data->number); | ||||
|         if(!strcmp(emv_data->name, "")) { | ||||
|             bank_card_set_cardholder_name(bank_card, emv_data->cardholder); | ||||
|         } | ||||
|         bank_card_set_back_callback(bank_card, nfc_scene_device_info_bank_card_callback, nfc); | ||||
|         if(emv_data->exp_mon) { | ||||
|             bank_card_set_exp_date(bank_card, emv_data->exp_mon, emv_data->exp_year); | ||||
|         } | ||||
|         if(emv_data->country_code) { | ||||
|             bank_card_set_country_name(bank_card, emv_data->country_code); | ||||
|         } | ||||
|         if(emv_data->currency_code) { | ||||
|             bank_card_set_currency_name(bank_card, emv_data->currency_code); | ||||
|         } | ||||
|     } | ||||
|     scene_manager_set_scene_state(nfc->scene_manager, NfcSceneDeviceInfo, NfcSceneDeviceInfoUid); | ||||
|     view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewWidget); | ||||
| } | ||||
| 
 | ||||
| const bool nfc_scene_device_info_on_event(void* context, SceneManagerEvent event) { | ||||
|     Nfc* nfc = (Nfc*)context; | ||||
|     Nfc* nfc = context; | ||||
|     bool consumed = false; | ||||
|     uint32_t state = scene_manager_get_scene_state(nfc->scene_manager, NfcSceneDeviceInfo); | ||||
| 
 | ||||
|  | ||||
| @ -5,7 +5,7 @@ const void nfc_scene_emulate_uid_on_enter(void* context) { | ||||
| 
 | ||||
|     // Setup view
 | ||||
|     Popup* popup = nfc->popup; | ||||
|     NfcDeviceCommomData* data = &nfc->dev.dev_data.nfc_data; | ||||
|     NfcDeviceCommonData* data = &nfc->dev.dev_data.nfc_data; | ||||
| 
 | ||||
|     if(strcmp(nfc->dev.dev_name, "")) { | ||||
|         nfc_text_store_set(nfc, "%s", nfc->dev.dev_name); | ||||
|  | ||||
| @ -18,7 +18,7 @@ void nfc_scene_read_card_success_on_enter(void* context) { | ||||
|     notification_message(nfc->notifications, &sequence_success); | ||||
| 
 | ||||
|     // Setup view
 | ||||
|     NfcDeviceCommomData* data = (NfcDeviceCommomData*)&nfc->dev.dev_data.nfc_data; | ||||
|     NfcDeviceCommonData* data = (NfcDeviceCommonData*)&nfc->dev.dev_data.nfc_data; | ||||
|     DialogEx* dialog_ex = nfc->dialog_ex; | ||||
|     dialog_ex_set_left_button_text(dialog_ex, "Retry"); | ||||
|     dialog_ex_set_right_button_text(dialog_ex, "More"); | ||||
|  | ||||
| @ -1,4 +1,5 @@ | ||||
| #include "../nfc_i.h" | ||||
| #include "../helpers/nfc_emv_parser.h" | ||||
| 
 | ||||
| #define NFC_SCENE_READ_SUCCESS_SHIFT "              " | ||||
| 
 | ||||
| @ -12,7 +13,7 @@ void nfc_scene_read_emv_app_success_on_enter(void* context) { | ||||
|     Nfc* nfc = (Nfc*)context; | ||||
| 
 | ||||
|     // Setup view
 | ||||
|     NfcDeviceCommomData* nfc_data = &nfc->dev.dev_data.nfc_data; | ||||
|     NfcDeviceCommonData* nfc_data = &nfc->dev.dev_data.nfc_data; | ||||
|     NfcEmvData* emv_data = &nfc->dev.dev_data.emv_data; | ||||
|     DialogEx* dialog_ex = nfc->dialog_ex; | ||||
|     dialog_ex_set_left_button_text(dialog_ex, "Retry"); | ||||
| @ -21,10 +22,13 @@ void nfc_scene_read_emv_app_success_on_enter(void* context) { | ||||
|     dialog_ex_set_icon(dialog_ex, 8, 13, &I_Medium_chip_22x21); | ||||
|     // Display UID and AID
 | ||||
|     string_t aid; | ||||
|     string_init_printf(aid, "AID:"); | ||||
|     string_init(aid); | ||||
|     bool aid_found = nfc_emv_parser_get_aid_name(emv_data->aid, emv_data->aid_len, aid); | ||||
|     if(!aid_found) { | ||||
|         for(uint8_t i = 0; i < emv_data->aid_len; i++) { | ||||
|             string_cat_printf(aid, " %02X", emv_data->aid[i]); | ||||
|         } | ||||
|     } | ||||
|     nfc_text_store_set( | ||||
|         nfc, | ||||
|         NFC_SCENE_READ_SUCCESS_SHIFT "UID: %02X %02X %02X %02X \n\n%s", | ||||
|  | ||||
| @ -16,6 +16,8 @@ const void nfc_scene_read_emv_data_on_enter(void* context) { | ||||
|     popup_set_icon(popup, 0, 3, &I_RFIDDolphinReceive_97x61); | ||||
| 
 | ||||
|     view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewPopup); | ||||
|     // Clear emv data
 | ||||
|     memset(&nfc->dev.dev_data.emv_data, 0, sizeof(nfc->dev.dev_data.emv_data)); | ||||
|     // Start worker
 | ||||
|     nfc_worker_start( | ||||
|         nfc->worker, | ||||
|  | ||||
| @ -1,4 +1,5 @@ | ||||
| #include "../nfc_i.h" | ||||
| #include "../helpers/nfc_emv_parser.h" | ||||
| 
 | ||||
| void nfc_scene_read_emv_data_success_widget_callback(GuiButtonType result, void* context) { | ||||
|     Nfc* nfc = (Nfc*)context; | ||||
| @ -9,12 +10,15 @@ void nfc_scene_read_emv_data_success_widget_callback(GuiButtonType result, void* | ||||
| void nfc_scene_read_emv_data_success_on_enter(void* context) { | ||||
|     Nfc* nfc = (Nfc*)context; | ||||
|     NfcEmvData* emv_data = &nfc->dev.dev_data.emv_data; | ||||
|     NfcDeviceCommomData* nfc_data = &nfc->dev.dev_data.nfc_data; | ||||
|     NfcDeviceCommonData* nfc_data = &nfc->dev.dev_data.nfc_data; | ||||
| 
 | ||||
|     // Clear device name
 | ||||
|     nfc_device_set_name(&nfc->dev, ""); | ||||
| 
 | ||||
|     // Setup Custom Widget view
 | ||||
|     // Add frame
 | ||||
|     widget_add_frame_element(nfc->widget, 0, 0, 128, 64, 6); | ||||
|     // Add buttons
 | ||||
|     widget_add_button_element( | ||||
|         nfc->widget, | ||||
|         GuiButtonTypeLeft, | ||||
| @ -27,8 +31,10 @@ void nfc_scene_read_emv_data_success_on_enter(void* context) { | ||||
|         "Save", | ||||
|         nfc_scene_read_emv_data_success_widget_callback, | ||||
|         nfc); | ||||
|     // Add card name
 | ||||
|     widget_add_string_element( | ||||
|         nfc->widget, 64, 3, AlignCenter, AlignTop, FontSecondary, nfc->dev.dev_data.emv_data.name); | ||||
|     // Add cad number
 | ||||
|     char pan_str[32]; | ||||
|     snprintf( | ||||
|         pan_str, | ||||
| @ -43,9 +49,41 @@ void nfc_scene_read_emv_data_success_on_enter(void* context) { | ||||
|         emv_data->number[6], | ||||
|         emv_data->number[7]); | ||||
|     widget_add_string_element(nfc->widget, 64, 13, AlignCenter, AlignTop, FontSecondary, pan_str); | ||||
|     // Parse country code
 | ||||
|     string_t country_name; | ||||
|     string_init(country_name); | ||||
|     if((emv_data->country_code) && | ||||
|        nfc_emv_parser_get_country_name(emv_data->country_code, country_name)) { | ||||
|         string_t disp_country; | ||||
|         string_init_printf(disp_country, "Reg:%s", country_name); | ||||
|         widget_add_string_element( | ||||
|             nfc->widget, 7, 23, AlignLeft, AlignTop, FontSecondary, string_get_cstr(disp_country)); | ||||
|         string_clear(disp_country); | ||||
|     } | ||||
|     string_clear(country_name); | ||||
|     // Parse currency code
 | ||||
|     string_t currency_name; | ||||
|     string_init(currency_name); | ||||
|     if((emv_data->currency_code) && | ||||
|        nfc_emv_parser_get_currency_name(emv_data->currency_code, currency_name)) { | ||||
|         string_t disp_currency; | ||||
|         string_init_printf(disp_currency, "Cur:%s", currency_name); | ||||
|         widget_add_string_element( | ||||
|             nfc->widget, | ||||
|             121, | ||||
|             23, | ||||
|             AlignRight, | ||||
|             AlignTop, | ||||
|             FontSecondary, | ||||
|             string_get_cstr(disp_currency)); | ||||
|         string_clear(disp_currency); | ||||
|     } | ||||
|     string_clear(currency_name); | ||||
|     // Add ATQA
 | ||||
|     char atqa_str[16]; | ||||
|     snprintf(atqa_str, sizeof(atqa_str), "ATQA: %02X%02X", nfc_data->atqa[0], nfc_data->atqa[1]); | ||||
|     widget_add_string_element(nfc->widget, 121, 32, AlignRight, AlignTop, FontSecondary, atqa_str); | ||||
|     // Add UID
 | ||||
|     char uid_str[32]; | ||||
|     snprintf( | ||||
|         uid_str, | ||||
| @ -56,9 +94,11 @@ void nfc_scene_read_emv_data_success_on_enter(void* context) { | ||||
|         nfc_data->uid[2], | ||||
|         nfc_data->uid[3]); | ||||
|     widget_add_string_element(nfc->widget, 7, 42, AlignLeft, AlignTop, FontSecondary, uid_str); | ||||
|     // Add SAK
 | ||||
|     char sak_str[16]; | ||||
|     snprintf(sak_str, sizeof(sak_str), "SAK: %02X", nfc_data->sak); | ||||
|     widget_add_string_element(nfc->widget, 121, 42, AlignRight, AlignTop, FontSecondary, sak_str); | ||||
|     // Add expiration date
 | ||||
|     if(emv_data->exp_mon) { | ||||
|         char exp_str[16]; | ||||
|         snprintf( | ||||
|  | ||||
| @ -30,7 +30,7 @@ const void nfc_scene_read_mifare_ul_success_on_enter(void* context) { | ||||
|     notification_message(nfc->notifications, &sequence_success); | ||||
| 
 | ||||
|     // Setup dialog view
 | ||||
|     NfcDeviceCommomData* data = (NfcDeviceCommomData*)&nfc->dev.dev_data.nfc_data; | ||||
|     NfcDeviceCommonData* data = (NfcDeviceCommonData*)&nfc->dev.dev_data.nfc_data; | ||||
|     DialogEx* dialog_ex = nfc->dialog_ex; | ||||
|     dialog_ex_set_left_button_text(dialog_ex, "Retry"); | ||||
|     dialog_ex_set_right_button_text(dialog_ex, "More"); | ||||
|  | ||||
| @ -1,5 +1,5 @@ | ||||
| #include "bank_card.h" | ||||
| #include <gui/modules/widget.h> | ||||
| #include "../helpers/nfc_emv_parser.h" | ||||
| #include <m-string.h> | ||||
| 
 | ||||
| struct BankCard { | ||||
| @ -43,10 +43,20 @@ void bank_card_set_number(BankCard* bank_card, uint8_t* number) { | ||||
|     for(uint8_t i = 0; i < 8; i += 2) { | ||||
|         string_cat_printf(num_str, "%02X%02X ", number[i], number[i + 1]); | ||||
|     } | ||||
|     // Add number
 | ||||
|     widget_add_string_element( | ||||
|         bank_card->widget, 25, 22, AlignLeft, AlignTop, FontSecondary, string_get_cstr(num_str)); | ||||
|     widget_add_icon_element(bank_card->widget, 6, 20, &I_EMV_Chip_14x11); | ||||
|         bank_card->widget, 64, 32, AlignCenter, AlignTop, FontSecondary, string_get_cstr(num_str)); | ||||
|     string_clear(num_str); | ||||
|     // Add icon
 | ||||
|     widget_add_icon_element(bank_card->widget, 8, 15, &I_Detailed_chip_17x13); | ||||
|     // Add frame
 | ||||
|     widget_add_frame_element(bank_card->widget, 0, 0, 128, 64, 6); | ||||
| } | ||||
| 
 | ||||
| void bank_card_set_back_callback(BankCard* bank_card, ButtonCallback callback, void* context) { | ||||
|     furi_assert(bank_card); | ||||
|     furi_assert(callback); | ||||
|     widget_add_button_element(bank_card->widget, GuiButtonTypeLeft, "Back", callback, context); | ||||
| } | ||||
| 
 | ||||
| void bank_card_set_exp_date(BankCard* bank_card, uint8_t mon, uint8_t year) { | ||||
| @ -57,8 +67,42 @@ void bank_card_set_exp_date(BankCard* bank_card, uint8_t mon, uint8_t year) { | ||||
|         bank_card->widget, 122, 54, AlignRight, AlignBottom, FontSecondary, exp_date_str); | ||||
| } | ||||
| 
 | ||||
| void bank_card_set_cardholder_name(BankCard* bank_card, char* name) { | ||||
| void bank_card_set_country_name(BankCard* bank_card, uint16_t country_code) { | ||||
|     furi_assert(bank_card); | ||||
|     furi_assert(name); | ||||
|     widget_add_string_element(bank_card->widget, 6, 37, AlignLeft, AlignTop, FontSecondary, name); | ||||
|     string_t country_name; | ||||
|     string_init(country_name); | ||||
|     if(nfc_emv_parser_get_country_name(country_code, country_name)) { | ||||
|         string_t disp_country; | ||||
|         string_init_printf(disp_country, "Reg:%s", country_name); | ||||
|         widget_add_string_element( | ||||
|             bank_card->widget, | ||||
|             120, | ||||
|             18, | ||||
|             AlignRight, | ||||
|             AlignTop, | ||||
|             FontSecondary, | ||||
|             string_get_cstr(disp_country)); | ||||
|         string_clear(disp_country); | ||||
|     } | ||||
|     string_clear(country_name); | ||||
| } | ||||
| 
 | ||||
| void bank_card_set_currency_name(BankCard* bank_card, uint16_t currency_code) { | ||||
|     furi_assert(bank_card); | ||||
|     string_t currency_name; | ||||
|     string_init(currency_name); | ||||
|     if(nfc_emv_parser_get_currency_name(currency_code, currency_name)) { | ||||
|         string_t disp_currency; | ||||
|         string_init_printf(disp_currency, "Cur:%s", currency_name); | ||||
|         widget_add_string_element( | ||||
|             bank_card->widget, | ||||
|             31, | ||||
|             18, | ||||
|             AlignLeft, | ||||
|             AlignTop, | ||||
|             FontSecondary, | ||||
|             string_get_cstr(disp_currency)); | ||||
|         string_clear(disp_currency); | ||||
|     } | ||||
|     string_clear(currency_name); | ||||
| } | ||||
|  | ||||
| @ -1,11 +1,10 @@ | ||||
| #pragma once | ||||
| #include <stdint.h> | ||||
| #include <gui/view.h> | ||||
| #include <gui/modules/widget.h> | ||||
| 
 | ||||
| typedef struct BankCard BankCard; | ||||
| 
 | ||||
| typedef void (*BankCardBackCallback)(void); | ||||
| 
 | ||||
| BankCard* bank_card_alloc(); | ||||
| 
 | ||||
| void bank_card_free(BankCard* bank_card); | ||||
| @ -14,10 +13,14 @@ void bank_card_clear(BankCard* bank_card); | ||||
| 
 | ||||
| View* bank_card_get_view(BankCard* bank_card); | ||||
| 
 | ||||
| void bank_card_set_back_callback(BankCard* bank_card, ButtonCallback callback, void* context); | ||||
| 
 | ||||
| void bank_card_set_name(BankCard* bank_card, char* name); | ||||
| 
 | ||||
| void bank_card_set_number(BankCard* bank_card, uint8_t* number); | ||||
| 
 | ||||
| void bank_card_set_exp_date(BankCard* bank_card, uint8_t mon, uint8_t year); | ||||
| 
 | ||||
| void bank_card_set_cardholder_name(BankCard* bank_card, char* name); | ||||
| void bank_card_set_country_name(BankCard* bank_card, uint16_t country_code); | ||||
| 
 | ||||
| void bank_card_set_currency_name(BankCard* bank_card, uint16_t currency_code); | ||||
|  | ||||
| @ -1,19 +0,0 @@ | ||||
| #include "../subghz_i.h" | ||||
| 
 | ||||
| const void subghz_scene_analyze_on_enter(void* context) { | ||||
|     SubGhz* subghz = context; | ||||
|     view_dispatcher_switch_to_view(subghz->view_dispatcher, SubGhzViewAnalyze); | ||||
| } | ||||
| 
 | ||||
| const bool subghz_scene_analyze_on_event(void* context, SceneManagerEvent event) { | ||||
|     SubGhz* subghz = context; | ||||
|     if(event.type == SceneManagerEventTypeTick) { | ||||
|         notification_message(subghz->notifications, &sequence_blink_blue_10); | ||||
|         return true; | ||||
|     } | ||||
|     return false; | ||||
| } | ||||
| 
 | ||||
| const void subghz_scene_analyze_on_exit(void* context) { | ||||
|     // SubGhz* subghz = context;
 | ||||
| } | ||||
| @ -1,13 +1,12 @@ | ||||
| ADD_SCENE(subghz, start, Start) | ||||
| ADD_SCENE(subghz, analyze, Analyze) | ||||
| ADD_SCENE(subghz, read, Read) | ||||
| ADD_SCENE(subghz, receiver, Receiver) | ||||
| ADD_SCENE(subghz, save_name, SaveName) | ||||
| ADD_SCENE(subghz, save_success, SaveSuccess) | ||||
| ADD_SCENE(subghz, saved, Saved) | ||||
| ADD_SCENE(subghz, transmitter, Transmitter) | ||||
| ADD_SCENE(subghz, static, Static) | ||||
| ADD_SCENE(subghz, no_man, NoMan) | ||||
| ADD_SCENE(subghz, test, Test) | ||||
| ADD_SCENE(subghz, test_static, TestStatic) | ||||
| ADD_SCENE(subghz, test_carrier, TestCarrier) | ||||
| ADD_SCENE(subghz, test_packet, TestPacket) | ||||
| ADD_SCENE(subghz, set_type, SetType) | ||||
|  | ||||
							
								
								
									
										48
									
								
								applications/subghz/scenes/subghz_scene_no_man.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @ -0,0 +1,48 @@ | ||||
| #include "../subghz_i.h" | ||||
| 
 | ||||
| #define SCENE_NO_MAN_CUSTOM_EVENT (11UL) | ||||
| 
 | ||||
| void subghz_scene_no_man_popup_callback(void* context) { | ||||
|     SubGhz* subghz = context; | ||||
|     view_dispatcher_send_custom_event(subghz->view_dispatcher, SCENE_NO_MAN_CUSTOM_EVENT); | ||||
| } | ||||
| 
 | ||||
| const void subghz_scene_no_man_on_enter(void* context) { | ||||
|     SubGhz* subghz = context; | ||||
| 
 | ||||
|     // Setup view
 | ||||
|     Popup* popup = subghz->popup; | ||||
|     popup_set_icon(popup, 32, 12, &I_DolphinFirstStart7_61x51); | ||||
|     popup_set_header(popup, "No manufactory key", 13, 8, AlignLeft, AlignBottom); | ||||
|     popup_set_timeout(popup, 1500); | ||||
|     popup_set_context(popup, subghz); | ||||
|     popup_set_callback(popup, subghz_scene_no_man_popup_callback); | ||||
|     popup_enable_timeout(popup); | ||||
|     view_dispatcher_switch_to_view(subghz->view_dispatcher, SubGhzViewPopup); | ||||
| } | ||||
| 
 | ||||
| const bool subghz_scene_no_man_on_event(void* context, SceneManagerEvent event) { | ||||
|     SubGhz* subghz = context; | ||||
|     if(event.type == SceneManagerEventTypeCustom) { | ||||
|         if(event.event == SCENE_NO_MAN_CUSTOM_EVENT) { | ||||
|             scene_manager_search_and_switch_to_previous_scene( | ||||
|                 subghz->scene_manager, SubGhzSceneStart); | ||||
|             return true; | ||||
|         } | ||||
|     } | ||||
|     return false; | ||||
| } | ||||
| 
 | ||||
| const void subghz_scene_no_man_on_exit(void* context) { | ||||
|     SubGhz* subghz = context; | ||||
| 
 | ||||
|     // Clear view
 | ||||
|     Popup* popup = subghz->popup; | ||||
|     popup_set_header(popup, NULL, 0, 0, AlignCenter, AlignBottom); | ||||
|     popup_set_text(popup, NULL, 0, 0, AlignCenter, AlignTop); | ||||
|     popup_set_icon(popup, 0, 0, NULL); | ||||
|     popup_set_callback(popup, NULL); | ||||
|     popup_set_context(popup, NULL); | ||||
|     popup_set_timeout(popup, 0); | ||||
|     popup_disable_timeout(popup); | ||||
| } | ||||
| @ -1,62 +0,0 @@ | ||||
| #include "../subghz_i.h" | ||||
| 
 | ||||
| #define GUBGHZ_READ_CUSTOM_EVENT (10UL) | ||||
| 
 | ||||
| void subghz_read_protocol_callback(SubGhzProtocolCommon* parser, void* context) { | ||||
|     furi_assert(context); | ||||
|     SubGhz* subghz = context; | ||||
|     subghz->protocol_result = parser; | ||||
|     view_dispatcher_send_custom_event(subghz->view_dispatcher, GUBGHZ_READ_CUSTOM_EVENT); | ||||
| } | ||||
| void subghz_scene_read_callback(DialogExResult result, void* context) { | ||||
|     SubGhz* subghz = context; | ||||
|     view_dispatcher_send_custom_event(subghz->view_dispatcher, result); | ||||
| } | ||||
| 
 | ||||
| const void subghz_scene_read_on_enter(void* context) { | ||||
|     SubGhz* subghz = context; | ||||
| 
 | ||||
|     // Setup view
 | ||||
|     DialogEx* dialog_ex = subghz->dialog_ex; | ||||
| 
 | ||||
|     dialog_ex_set_header(dialog_ex, "SubGhz 433.92", 36, 6, AlignLeft, AlignCenter); | ||||
|     dialog_ex_set_icon(dialog_ex, 10, 12, &I_RFIDDolphinReceive_97x61); | ||||
| 
 | ||||
|     //Start CC1101 rx
 | ||||
|     subghz_begin(FuriHalSubGhzPresetOokAsync); | ||||
|     subghz_rx(433920000); | ||||
| 
 | ||||
|     furi_hal_subghz_start_async_rx(subghz_worker_rx_callback, subghz->worker); | ||||
|     subghz_worker_start(subghz->worker); | ||||
|     subghz_protocol_enable_dump(subghz->protocol, subghz_read_protocol_callback, subghz); | ||||
| 
 | ||||
|     view_dispatcher_switch_to_view(subghz->view_dispatcher, SubGhzViewDialogEx); | ||||
| } | ||||
| 
 | ||||
| const bool subghz_scene_read_on_event(void* context, SceneManagerEvent event) { | ||||
|     SubGhz* subghz = context; | ||||
|     if(event.type == SceneManagerEventTypeCustom) { | ||||
|         if(event.event == GUBGHZ_READ_CUSTOM_EVENT) { | ||||
|             scene_manager_next_scene(subghz->scene_manager, SubGhzSceneReceiver); | ||||
|             notification_message(subghz->notifications, &sequence_success); | ||||
|             return true; | ||||
|         } | ||||
|     } else if(event.type == SceneManagerEventTypeTick) { | ||||
|         notification_message(subghz->notifications, &sequence_blink_blue_10); | ||||
|         return true; | ||||
|     } | ||||
|     return false; | ||||
| } | ||||
| 
 | ||||
| const void subghz_scene_read_on_exit(void* context) { | ||||
|     SubGhz* subghz = context; | ||||
| 
 | ||||
|     // Stop CC1101
 | ||||
|     subghz_worker_stop(subghz->worker); | ||||
|     furi_hal_subghz_stop_async_rx(); | ||||
|     subghz_end(); | ||||
| 
 | ||||
|     DialogEx* dialog_ex = subghz->dialog_ex; | ||||
|     dialog_ex_set_header(dialog_ex, NULL, 0, 0, AlignCenter, AlignCenter); | ||||
|     dialog_ex_set_icon(dialog_ex, 0, 0, NULL); | ||||
| } | ||||
| @ -13,7 +13,9 @@ const void subghz_scene_receiver_on_enter(void* context) { | ||||
| 
 | ||||
|     subghz_receiver_set_callback(subghz_receiver, subghz_scene_receiver_callback, subghz); | ||||
| 
 | ||||
|     subghz_receiver_set_protocol(subghz_receiver, subghz->protocol_result); | ||||
|     subghz_receiver_set_protocol(subghz_receiver, subghz->protocol_result, subghz->protocol); | ||||
|     subghz_receiver_set_worker(subghz_receiver, subghz->worker); | ||||
|     subghz->state_notifications = NOTIFICATION_RX_STATE; | ||||
|     view_dispatcher_switch_to_view(subghz->view_dispatcher, SubGhzViewReceiver); | ||||
| } | ||||
| 
 | ||||
| @ -21,12 +23,57 @@ const bool subghz_scene_receiver_on_event(void* context, SceneManagerEvent event | ||||
|     SubGhz* subghz = context; | ||||
| 
 | ||||
|     if(event.type == SceneManagerEventTypeCustom) { | ||||
|         if(event.event == SubghzReceverEventSave) { | ||||
|         switch(event.event) { | ||||
|         case SubghzReceverEventSave: | ||||
|             subghz->state_notifications = NOTIFICATION_IDLE_STATE; | ||||
|             subghz->frequency = subghz_receiver_get_frequency(subghz->subghz_receiver); | ||||
|             subghz->preset = subghz_receiver_get_preset(subghz->subghz_receiver); | ||||
|             subghz->protocol_result = subghz_receiver_get_protocol(subghz->subghz_receiver); | ||||
|             scene_manager_next_scene(subghz->scene_manager, SubGhzSceneSaveName); | ||||
|             return true; | ||||
|         } else if(event.event == SubghzReceverEventBack) { | ||||
|             break; | ||||
|         case SubghzReceverEventBack: | ||||
|             scene_manager_previous_scene(subghz->scene_manager); | ||||
|             return true; | ||||
|             break; | ||||
|         case SubghzReceverEventSendStart: | ||||
|             subghz->state_notifications = NOTIFICATION_TX_STATE; | ||||
|             subghz->frequency = subghz_receiver_get_frequency(subghz->subghz_receiver); | ||||
|             subghz->preset = subghz_receiver_get_preset(subghz->subghz_receiver); | ||||
|             subghz->protocol_result = subghz_receiver_get_protocol(subghz->subghz_receiver); | ||||
|             subghz_transmitter_tx_start(subghz); | ||||
|             return true; | ||||
|             break; | ||||
|         case SubghzReceverEventSendStop: | ||||
|             subghz->state_notifications = NOTIFICATION_IDLE_STATE; | ||||
|             subghz_transmitter_tx_stop(subghz); | ||||
|             return true; | ||||
|             break; | ||||
|         case SubghzReceverEventMain: | ||||
|             subghz->state_notifications = NOTIFICATION_RX_STATE; | ||||
|             return true; | ||||
|             break; | ||||
|         case SubghzReceverEventConfig: | ||||
|             subghz->state_notifications = NOTIFICATION_IDLE_STATE; | ||||
|             return true; | ||||
|             break; | ||||
|         case SubghzReceverEventSendHistoryFull: | ||||
|             subghz->state_notifications = NOTIFICATION_IDLE_STATE; | ||||
|             return true; | ||||
|             break; | ||||
|         default: | ||||
|             break; | ||||
|         } | ||||
|     } else if(event.type == SceneManagerEventTypeTick) { | ||||
|         switch(subghz->state_notifications) { | ||||
|         case NOTIFICATION_TX_STATE: | ||||
|             notification_message(subghz->notifications, &sequence_blink_red_10); | ||||
|             break; | ||||
|         case NOTIFICATION_RX_STATE: | ||||
|             notification_message(subghz->notifications, &sequence_blink_blue_10); | ||||
|             break; | ||||
|         default: | ||||
|             break; | ||||
|         } | ||||
|     } | ||||
|     return false; | ||||
|  | ||||
| @ -19,7 +19,7 @@ const void subghz_scene_save_name_on_enter(void* context) { | ||||
|     set_random_name(subghz->text_store, sizeof(subghz->text_store)); | ||||
|     dev_name_empty = true; | ||||
| 
 | ||||
|     text_input_set_header_text(text_input, "Name the KEY"); | ||||
|     text_input_set_header_text(text_input, "Name signal"); | ||||
|     text_input_set_result_callback( | ||||
|         text_input, | ||||
|         subghz_scene_save_name_text_input_callback, | ||||
|  | ||||
| @ -25,9 +25,13 @@ const bool subghz_scene_save_success_on_event(void* context, SceneManagerEvent e | ||||
|     SubGhz* subghz = context; | ||||
|     if(event.type == SceneManagerEventTypeCustom) { | ||||
|         if(event.event == SCENE_SAVE_SUCCESS_CUSTOM_EVENT) { | ||||
|             return scene_manager_search_and_switch_to_previous_scene( | ||||
|             if(!scene_manager_search_and_switch_to_previous_scene( | ||||
|                    subghz->scene_manager, SubGhzSceneReceiver)) { | ||||
|                 scene_manager_search_and_switch_to_previous_scene( | ||||
|                     subghz->scene_manager, SubGhzSceneStart); | ||||
|             } | ||||
|             return true; | ||||
|         } | ||||
|     } | ||||
|     return false; | ||||
| } | ||||
|  | ||||
| @ -3,7 +3,7 @@ | ||||
| const void subghz_scene_saved_on_enter(void* context) { | ||||
|     SubGhz* subghz = context; | ||||
| 
 | ||||
|     if(subghz_saved_protocol_select(subghz)) { | ||||
|     if(subghz_load_protocol_from_file(subghz)) { | ||||
|         scene_manager_next_scene(subghz->scene_manager, SubGhzSceneTransmitter); | ||||
|     } else { | ||||
|         scene_manager_search_and_switch_to_previous_scene(subghz->scene_manager, SubGhzSceneStart); | ||||
|  | ||||
| @ -32,31 +32,31 @@ const void subghz_scene_set_type_on_enter(void* context) { | ||||
| 
 | ||||
|     submenu_add_item( | ||||
|         subghz->submenu, | ||||
|         "Pricenton", | ||||
|         "Princeton_433", | ||||
|         SubmenuIndexPricenton, | ||||
|         subghz_scene_set_type_submenu_callback, | ||||
|         subghz); | ||||
|     submenu_add_item( | ||||
|         subghz->submenu, | ||||
|         "Nice Flo 12bit", | ||||
|         "Nice Flo 12bit_433", | ||||
|         SubmenuIndexNiceFlo12bit, | ||||
|         subghz_scene_set_type_submenu_callback, | ||||
|         subghz); | ||||
|     submenu_add_item( | ||||
|         subghz->submenu, | ||||
|         "Nice Flo 24bit", | ||||
|         "Nice Flo 24bit_433", | ||||
|         SubmenuIndexNiceFlo24bit, | ||||
|         subghz_scene_set_type_submenu_callback, | ||||
|         subghz); | ||||
|     submenu_add_item( | ||||
|         subghz->submenu, | ||||
|         "CAME 12bit", | ||||
|         "CAME 12bit_433", | ||||
|         SubmenuIndexCAME12bit, | ||||
|         subghz_scene_set_type_submenu_callback, | ||||
|         subghz); | ||||
|     submenu_add_item( | ||||
|         subghz->submenu, | ||||
|         "CAME 24bit", | ||||
|         "CAME 24bit_433", | ||||
|         SubmenuIndexCAME24bit, | ||||
|         subghz_scene_set_type_submenu_callback, | ||||
|         subghz); | ||||
| @ -64,13 +64,13 @@ const void subghz_scene_set_type_on_enter(void* context) { | ||||
|     //     subghz->submenu, "Nero Sketch", SubmenuIndexNeroSketch, subghz_scene_set_type_submenu_callback, subghz);
 | ||||
|     submenu_add_item( | ||||
|         subghz->submenu, | ||||
|         "Gate TX", | ||||
|         "Gate TX_433", | ||||
|         SubmenuIndexGateTX, | ||||
|         subghz_scene_set_type_submenu_callback, | ||||
|         subghz); | ||||
|     submenu_add_item( | ||||
|         subghz->submenu, | ||||
|         "DoorHan", | ||||
|         "DoorHan_433", | ||||
|         SubmenuIndexDoorHan, | ||||
|         subghz_scene_set_type_submenu_callback, | ||||
|         subghz); | ||||
| @ -146,11 +146,15 @@ const bool subghz_scene_set_type_on_event(void* context, SceneManagerEvent event | ||||
|                 subghz->protocol_result->serial = key & 0x0FFFFFFF; | ||||
|                 subghz->protocol_result->btn = 0x2; //btn 0x1, 0x2, 0x4, 0x8
 | ||||
|                 subghz->protocol_result->cnt = 0x0003; | ||||
|                 subghz_protocol_keeloq_set_manufacture_name(subghz->protocol_result, "DoorHan"); | ||||
|                 if(subghz_protocol_keeloq_set_manufacture_name( | ||||
|                        subghz->protocol_result, "DoorHan")) { | ||||
|                     subghz->protocol_result->code_last_found = | ||||
|                         subghz_protocol_keeloq_gen_key(subghz->protocol_result); | ||||
| 
 | ||||
|                     generated_protocol = true; | ||||
|                 } else { | ||||
|                     generated_protocol = false; | ||||
|                     scene_manager_next_scene(subghz->scene_manager, SubGhzSceneNoMan); | ||||
|                 } | ||||
|             } | ||||
|             break; | ||||
| 
 | ||||
| @ -159,6 +163,8 @@ const bool subghz_scene_set_type_on_event(void* context, SceneManagerEvent event | ||||
|             break; | ||||
|         } | ||||
|         if(generated_protocol) { | ||||
|             subghz->frequency = subghz_frequencies[subghz_frequencies_433_92]; | ||||
|             subghz->preset = FuriHalSubGhzPresetOok650Async; | ||||
|             scene_manager_next_scene(subghz->scene_manager, SubGhzSceneSaveName); | ||||
|             return true; | ||||
|         } | ||||
|  | ||||
| @ -1,10 +1,8 @@ | ||||
| #include "../subghz_i.h" | ||||
| 
 | ||||
| enum SubmenuIndex { | ||||
|     SubmenuIndexAnalyze, | ||||
|     SubmenuIndexRead, | ||||
|     SubmenuIndexSaved, | ||||
|     SubmenuIndexStatic, | ||||
|     SubmenuIndexTest, | ||||
|     SubmenuIndexAddManualy, | ||||
| }; | ||||
| @ -19,12 +17,6 @@ const void subghz_scene_start_on_enter(void* context) { | ||||
|     if(subghz->state_notifications == NOTIFICATION_STARTING_STATE) { | ||||
|         subghz->state_notifications = NOTIFICATION_IDLE_STATE; | ||||
|     } | ||||
|     submenu_add_item( | ||||
|         subghz->submenu, | ||||
|         "Analyze", | ||||
|         SubmenuIndexAnalyze, | ||||
|         subghz_scene_start_submenu_callback, | ||||
|         subghz); | ||||
|     submenu_add_item( | ||||
|         subghz->submenu, "Read", SubmenuIndexRead, subghz_scene_start_submenu_callback, subghz); | ||||
|     submenu_add_item( | ||||
| @ -35,8 +27,6 @@ const void subghz_scene_start_on_enter(void* context) { | ||||
|         SubmenuIndexAddManualy, | ||||
|         subghz_scene_start_submenu_callback, | ||||
|         subghz); | ||||
|     submenu_add_item( | ||||
|         subghz->submenu, "Static", SubmenuIndexStatic, subghz_scene_start_submenu_callback, subghz); | ||||
|     submenu_add_item( | ||||
|         subghz->submenu, "Test", SubmenuIndexTest, subghz_scene_start_submenu_callback, subghz); | ||||
| 
 | ||||
| @ -50,15 +40,10 @@ const bool subghz_scene_start_on_event(void* context, SceneManagerEvent event) { | ||||
|     SubGhz* subghz = context; | ||||
| 
 | ||||
|     if(event.type == SceneManagerEventTypeCustom) { | ||||
|         if(event.event == SubmenuIndexAnalyze) { | ||||
|             scene_manager_set_scene_state( | ||||
|                 subghz->scene_manager, SubGhzSceneStart, SubmenuIndexAnalyze); | ||||
|             scene_manager_next_scene(subghz->scene_manager, SubGhzSceneAnalyze); | ||||
|             return true; | ||||
|         } else if(event.event == SubmenuIndexRead) { | ||||
|         if(event.event == SubmenuIndexRead) { | ||||
|             scene_manager_set_scene_state( | ||||
|                 subghz->scene_manager, SubGhzSceneStart, SubmenuIndexRead); | ||||
|             scene_manager_next_scene(subghz->scene_manager, SubGhzSceneRead); | ||||
|             scene_manager_next_scene(subghz->scene_manager, SubGhzSceneReceiver); | ||||
|             return true; | ||||
|         } else if(event.event == SubmenuIndexSaved) { | ||||
|             scene_manager_set_scene_state( | ||||
| @ -70,11 +55,6 @@ const bool subghz_scene_start_on_event(void* context, SceneManagerEvent event) { | ||||
|                 subghz->scene_manager, SubGhzSceneStart, SubmenuIndexAddManualy); | ||||
|             scene_manager_next_scene(subghz->scene_manager, SubGhzSceneSetType); | ||||
|             return true; | ||||
|         } else if(event.event == SubmenuIndexStatic) { | ||||
|             scene_manager_set_scene_state( | ||||
|                 subghz->scene_manager, SubGhzSceneStart, SubmenuIndexStatic); | ||||
|             scene_manager_next_scene(subghz->scene_manager, SubGhzSceneStatic); | ||||
|             return true; | ||||
|         } else if(event.event == SubmenuIndexTest) { | ||||
|             scene_manager_set_scene_state( | ||||
|                 subghz->scene_manager, SubGhzSceneStart, SubmenuIndexTest); | ||||
|  | ||||
| @ -3,6 +3,7 @@ | ||||
| enum SubmenuIndex { | ||||
|     SubmenuIndexCarrier, | ||||
|     SubmenuIndexPacket, | ||||
|     SubmenuIndexStatic, | ||||
| }; | ||||
| 
 | ||||
| void subghz_scene_test_submenu_callback(void* context, uint32_t index) { | ||||
| @ -21,6 +22,8 @@ const void subghz_scene_test_on_enter(void* context) { | ||||
|         subghz); | ||||
|     submenu_add_item( | ||||
|         subghz->submenu, "Packet", SubmenuIndexPacket, subghz_scene_test_submenu_callback, subghz); | ||||
|     submenu_add_item( | ||||
|         subghz->submenu, "Static", SubmenuIndexStatic, subghz_scene_test_submenu_callback, subghz); | ||||
| 
 | ||||
|     submenu_set_selected_item( | ||||
|         subghz->submenu, scene_manager_get_scene_state(subghz->scene_manager, SubGhzSceneTest)); | ||||
| @ -42,6 +45,11 @@ const bool subghz_scene_test_on_event(void* context, SceneManagerEvent event) { | ||||
|                 subghz->scene_manager, SubGhzSceneTest, SubmenuIndexPacket); | ||||
|             scene_manager_next_scene(subghz->scene_manager, SubGhzSceneTestPacket); | ||||
|             return true; | ||||
|         } else if(event.event == SubmenuIndexStatic) { | ||||
|             scene_manager_set_scene_state( | ||||
|                 subghz->scene_manager, SubGhzSceneTest, SubmenuIndexStatic); | ||||
|             scene_manager_next_scene(subghz->scene_manager, SubGhzSceneTestStatic); | ||||
|             return true; | ||||
|         } | ||||
|     } | ||||
|     return false; | ||||
|  | ||||
| @ -1,15 +1,15 @@ | ||||
| #include "../subghz_i.h" | ||||
| 
 | ||||
| const void subghz_scene_static_on_enter(void* context) { | ||||
| const void subghz_scene_test_static_on_enter(void* context) { | ||||
|     SubGhz* subghz = context; | ||||
|     view_dispatcher_switch_to_view(subghz->view_dispatcher, SubGhzViewStatic); | ||||
| } | ||||
| 
 | ||||
| const bool subghz_scene_static_on_event(void* context, SceneManagerEvent event) { | ||||
| const bool subghz_scene_test_static_on_event(void* context, SceneManagerEvent event) { | ||||
|     // SubGhz* subghz = context;
 | ||||
|     return false; | ||||
| } | ||||
| 
 | ||||
| const void subghz_scene_static_on_exit(void* context) { | ||||
| const void subghz_scene_test_static_on_exit(void* context) { | ||||
|     // SubGhz* subghz = context;
 | ||||
| } | ||||
| @ -13,6 +13,7 @@ const void subghz_scene_transmitter_on_enter(void* context) { | ||||
| 
 | ||||
|     subghz_transmitter_set_callback(subghz_transmitter, subghz_scene_transmitter_callback, subghz); | ||||
|     subghz_transmitter_set_protocol(subghz_transmitter, subghz->protocol_result); | ||||
|     subghz_transmitter_set_frequency_preset(subghz_transmitter, subghz->frequency, subghz->preset); | ||||
| 
 | ||||
|     view_dispatcher_switch_to_view(subghz->view_dispatcher, SubGhzViewTransmitter); | ||||
| 
 | ||||
| @ -30,12 +31,18 @@ const bool subghz_scene_transmitter_on_event(void* context, SceneManagerEvent ev | ||||
|         } else if(event.event == SubghzTransmitterEventSendStop) { | ||||
|             subghz->state_notifications = NOTIFICATION_IDLE_STATE; | ||||
|             subghz_transmitter_tx_stop(subghz); | ||||
|             subghz_sleep(); | ||||
|             return true; | ||||
|         } else if(event.event == SubghzTransmitterEventBack) { | ||||
|             subghz->state_notifications = NOTIFICATION_IDLE_STATE; | ||||
|             scene_manager_search_and_switch_to_previous_scene( | ||||
|                 subghz->scene_manager, SubGhzSceneStart); | ||||
|             return true; | ||||
|         } else if(event.event == SubghzTransmitterEventNoMan) { | ||||
|             subghz->state_notifications = NOTIFICATION_IDLE_STATE; | ||||
|             scene_manager_search_and_switch_to_previous_scene( | ||||
|                 subghz->scene_manager, SubGhzSceneNoMan); | ||||
|             return true; | ||||
|         } | ||||
|     } else if(event.type == SceneManagerEventTypeTick) { | ||||
|         if(subghz->state_notifications == NOTIFICATION_TX_STATE) { | ||||
|  | ||||
| @ -70,13 +70,6 @@ SubGhz* subghz_alloc() { | ||||
|     view_dispatcher_add_view( | ||||
|         subghz->view_dispatcher, SubGhzViewMenu, submenu_get_view(subghz->submenu)); | ||||
| 
 | ||||
|     // Analyze
 | ||||
|     subghz->subghz_analyze = subghz_analyze_alloc(); | ||||
|     view_dispatcher_add_view( | ||||
|         subghz->view_dispatcher, | ||||
|         SubGhzViewAnalyze, | ||||
|         subghz_analyze_get_view(subghz->subghz_analyze)); | ||||
| 
 | ||||
|     // Receiver
 | ||||
|     subghz->subghz_receiver = subghz_receiver_alloc(); | ||||
|     view_dispatcher_add_view( | ||||
| @ -121,9 +114,11 @@ SubGhz* subghz_alloc() { | ||||
|         subghz_test_packet_get_view(subghz->subghz_test_packet)); | ||||
| 
 | ||||
|     // Static send
 | ||||
|     subghz->subghz_static = subghz_static_alloc(); | ||||
|     subghz->subghz_test_static = subghz_test_static_alloc(); | ||||
|     view_dispatcher_add_view( | ||||
|         subghz->view_dispatcher, SubGhzViewStatic, subghz_static_get_view(subghz->subghz_static)); | ||||
|         subghz->view_dispatcher, | ||||
|         SubGhzViewStatic, | ||||
|         subghz_test_static_get_view(subghz->subghz_test_static)); | ||||
| 
 | ||||
|     //init Worker & Protocol
 | ||||
|     subghz->worker = subghz_worker_alloc(); | ||||
| @ -134,8 +129,8 @@ SubGhz* subghz_alloc() { | ||||
|         subghz->worker, (SubGhzWorkerPairCallback)subghz_protocol_parse); | ||||
|     subghz_worker_set_context(subghz->worker, subghz->protocol); | ||||
| 
 | ||||
|     subghz_protocol_load_keeloq_file(subghz->protocol, "/ext/assets/subghz/keeloq_mfcodes"); | ||||
|     subghz_protocol_load_nice_flor_s_file(subghz->protocol, "/ext/assets/subghz/nice_floor_s_rx"); | ||||
|     subghz_protocol_load_keeloq_file(subghz->protocol, "/ext/subghz/keeloq_mfcodes"); | ||||
|     subghz_protocol_load_nice_flor_s_file(subghz->protocol, "/ext/subghz/nice_floor_s_rx"); | ||||
| 
 | ||||
|     //subghz_protocol_enable_dump_text(subghz->protocol, subghz_text_callback, subghz);
 | ||||
| 
 | ||||
| @ -155,11 +150,7 @@ void subghz_free(SubGhz* subghz) { | ||||
| 
 | ||||
|     // Static
 | ||||
|     view_dispatcher_remove_view(subghz->view_dispatcher, SubGhzViewStatic); | ||||
|     subghz_static_free(subghz->subghz_static); | ||||
| 
 | ||||
|     // Analyze
 | ||||
|     view_dispatcher_remove_view(subghz->view_dispatcher, SubGhzViewAnalyze); | ||||
|     subghz_analyze_free(subghz->subghz_analyze); | ||||
|     subghz_test_static_free(subghz->subghz_test_static); | ||||
| 
 | ||||
|     // Receiver
 | ||||
|     view_dispatcher_remove_view(subghz->view_dispatcher, SubGhzViewReceiver); | ||||
|  | ||||
| @ -42,7 +42,7 @@ void subghz_cli_command_tx_carrier(Cli* cli, string_t args, void* context) { | ||||
|     } | ||||
| 
 | ||||
|     furi_hal_subghz_reset(); | ||||
|     furi_hal_subghz_load_preset(FuriHalSubGhzPresetOokAsync); | ||||
|     furi_hal_subghz_load_preset(FuriHalSubGhzPresetOok650Async); | ||||
|     frequency = furi_hal_subghz_set_frequency_and_path(frequency); | ||||
| 
 | ||||
|     hal_gpio_init(&gpio_cc1101_g0, GpioModeOutputPushPull, GpioPullNo, GpioSpeedLow); | ||||
| @ -79,7 +79,7 @@ void subghz_cli_command_rx_carrier(Cli* cli, string_t args, void* context) { | ||||
|     } | ||||
| 
 | ||||
|     furi_hal_subghz_reset(); | ||||
|     furi_hal_subghz_load_preset(FuriHalSubGhzPresetOokAsync); | ||||
|     furi_hal_subghz_load_preset(FuriHalSubGhzPresetOok650Async); | ||||
|     frequency = furi_hal_subghz_set_frequency_and_path(frequency); | ||||
|     printf("Receiving at frequency %lu Hz\r\n", frequency); | ||||
|     printf("Press CTRL+C to stop\r\n"); | ||||
| @ -134,12 +134,12 @@ void subghz_cli_command_tx(Cli* cli, string_t args, void* context) { | ||||
|     protocol->common.code_last_found = key; | ||||
|     protocol->common.code_last_count_bit = 24; | ||||
| 
 | ||||
|     SubGhzProtocolEncoderCommon* encoder = subghz_protocol_encoder_common_alloc(); | ||||
|     SubGhzProtocolCommonEncoder* encoder = subghz_protocol_encoder_common_alloc(); | ||||
|     encoder->repeat = repeat; | ||||
| 
 | ||||
|     subghz_protocol_princeton_send_key(protocol, encoder); | ||||
|     furi_hal_subghz_reset(); | ||||
|     furi_hal_subghz_load_preset(FuriHalSubGhzPresetOokAsync); | ||||
|     furi_hal_subghz_load_preset(FuriHalSubGhzPresetOok650Async); | ||||
|     frequency = furi_hal_subghz_set_frequency_and_path(frequency); | ||||
|     furi_hal_subghz_start_async_tx(subghz_protocol_encoder_common_yield, encoder); | ||||
| 
 | ||||
| @ -206,13 +206,13 @@ void subghz_cli_command_rx(Cli* cli, string_t args, void* context) { | ||||
|     furi_check(instance->stream); | ||||
| 
 | ||||
|     SubGhzProtocol* protocol = subghz_protocol_alloc(); | ||||
|     subghz_protocol_load_keeloq_file(protocol, "/ext/assets/subghz/keeloq_mfcodes"); | ||||
|     subghz_protocol_load_nice_flor_s_file(protocol, "/ext/assets/subghz/nice_floor_s_rx"); | ||||
|     subghz_protocol_load_keeloq_file(protocol, "/ext/subghz/keeloq_mfcodes"); | ||||
|     subghz_protocol_load_nice_flor_s_file(protocol, "/ext/subghz/nice_floor_s_rx"); | ||||
|     subghz_protocol_enable_dump_text(protocol, subghz_cli_command_rx_text_callback, instance); | ||||
| 
 | ||||
|     // Configure radio
 | ||||
|     furi_hal_subghz_reset(); | ||||
|     furi_hal_subghz_load_preset(FuriHalSubGhzPresetOokAsync); | ||||
|     furi_hal_subghz_load_preset(FuriHalSubGhzPresetOok650Async); | ||||
|     frequency = furi_hal_subghz_set_frequency_and_path(frequency); | ||||
|     hal_gpio_init(&gpio_cc1101_g0, GpioModeInput, GpioPullNo, GpioSpeedLow); | ||||
| 
 | ||||
|  | ||||
							
								
								
									
										164
									
								
								applications/subghz/subghz_history.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @ -0,0 +1,164 @@ | ||||
| #include "subghz_history.h" | ||||
| #include <lib/subghz/protocols/subghz_protocol_keeloq.h> | ||||
| #include <lib/subghz/protocols/subghz_protocol_star_line.h> | ||||
| #include <lib/subghz/protocols/subghz_protocol_princeton.h> | ||||
| 
 | ||||
| #include <furi.h> | ||||
| #include <m-string.h> | ||||
| 
 | ||||
| #define SUBGHZ_HISTORY_MAX 50 | ||||
| 
 | ||||
| typedef struct SubGhzHistoryStruct SubGhzHistoryStruct; | ||||
| 
 | ||||
| struct SubGhzHistoryStruct { | ||||
|     const char* name; | ||||
|     const char* manufacture_name; | ||||
|     uint8_t type_protocol; | ||||
|     uint8_t code_count_bit; | ||||
|     uint64_t code_found; | ||||
|     uint16_t te; | ||||
|     FuriHalSubGhzPreset preset; | ||||
|     uint32_t real_frequency; | ||||
| }; | ||||
| 
 | ||||
| struct SubGhzHistory { | ||||
|     uint32_t last_update_timestamp; | ||||
|     uint16_t last_index_write; | ||||
|     uint64_t code_last_found; | ||||
|     SubGhzHistoryStruct history[SUBGHZ_HISTORY_MAX]; | ||||
|     SubGhzProtocolCommonLoad data; | ||||
| }; | ||||
| 
 | ||||
| SubGhzHistory* subghz_history_alloc(void) { | ||||
|     SubGhzHistory* instance = furi_alloc(sizeof(SubGhzHistory)); | ||||
|     return instance; | ||||
| } | ||||
| 
 | ||||
| void subghz_history_free(SubGhzHistory* instance) { | ||||
|     furi_assert(instance); | ||||
|     free(instance); | ||||
| } | ||||
| 
 | ||||
| void subghz_history_set_frequency_preset( | ||||
|     SubGhzHistory* instance, | ||||
|     uint16_t idx, | ||||
|     uint32_t frequency, | ||||
|     FuriHalSubGhzPreset preset) { | ||||
|     furi_assert(instance); | ||||
|     if(instance->last_index_write >= SUBGHZ_HISTORY_MAX) return; | ||||
|     instance->history[idx].preset = preset; | ||||
|     instance->history[idx].real_frequency = frequency; | ||||
| } | ||||
| 
 | ||||
| uint32_t subghz_history_get_frequency(SubGhzHistory* instance, uint16_t idx) { | ||||
|     furi_assert(instance); | ||||
|     return instance->history[idx].real_frequency; | ||||
| } | ||||
| 
 | ||||
| FuriHalSubGhzPreset subghz_history_get_preset(SubGhzHistory* instance, uint16_t idx) { | ||||
|     furi_assert(instance); | ||||
|     return instance->history[idx].preset; | ||||
| } | ||||
| 
 | ||||
| void subghz_history_clean(SubGhzHistory* instance) { | ||||
|     furi_assert(instance); | ||||
|     instance->last_index_write = 0; | ||||
|     instance->code_last_found = 0; | ||||
| } | ||||
| 
 | ||||
| uint16_t subghz_history_get_item(SubGhzHistory* instance) { | ||||
|     furi_assert(instance); | ||||
|     return instance->last_index_write; | ||||
| } | ||||
| 
 | ||||
| uint8_t subghz_history_get_type_protocol(SubGhzHistory* instance, uint16_t idx) { | ||||
|     furi_assert(instance); | ||||
|     return instance->history[idx].type_protocol; | ||||
| } | ||||
| 
 | ||||
| const char* subghz_history_get_name(SubGhzHistory* instance, uint16_t idx) { | ||||
|     furi_assert(instance); | ||||
|     return instance->history[idx].name; | ||||
| } | ||||
| 
 | ||||
| SubGhzProtocolCommonLoad* subghz_history_get_raw_data(SubGhzHistory* instance, uint16_t idx) { | ||||
|     furi_assert(instance); | ||||
|     instance->data.code_found = instance->history[idx].code_found; | ||||
|     instance->data.code_count_bit = instance->history[idx].code_count_bit; | ||||
|     instance->data.param1 = instance->history[idx].te; | ||||
|     return &instance->data; | ||||
| } | ||||
| bool subghz_history_get_text_space_left(SubGhzHistory* instance, string_t output) { | ||||
|     furi_assert(instance); | ||||
|     if(instance->last_index_write == SUBGHZ_HISTORY_MAX) { | ||||
|         if(output != NULL) string_printf(output, "Memory is FULL"); | ||||
|         return true; | ||||
|     } | ||||
|     if(output != NULL) | ||||
|         string_printf(output, "%02u/%02u", instance->last_index_write, SUBGHZ_HISTORY_MAX); | ||||
|     return false; | ||||
| } | ||||
| void subghz_history_get_text_item_menu(SubGhzHistory* instance, string_t output, uint16_t idx) { | ||||
|     if(instance->history[idx].code_count_bit < 33) { | ||||
|         string_printf( | ||||
|             output, | ||||
|             "%s %lX", | ||||
|             instance->history[idx].name, | ||||
|             (uint32_t)(instance->history[idx].code_found & 0xFFFFFFFF)); | ||||
|     } else { | ||||
|         string_t str_buff; | ||||
|         string_init(str_buff); | ||||
|         if(strcmp(instance->history[idx].name, "KeeLoq") == 0) { | ||||
|             string_set(str_buff, "KL "); | ||||
|             string_cat(str_buff, instance->history[idx].manufacture_name); | ||||
|         } else if(strcmp(instance->history[idx].name, "Star Line") == 0) { | ||||
|             string_set(str_buff, "SL "); | ||||
|             string_cat(str_buff, instance->history[idx].manufacture_name); | ||||
|         } else { | ||||
|             string_set(str_buff, instance->history[idx].name); | ||||
|         } | ||||
| 
 | ||||
|         string_printf( | ||||
|             output, | ||||
|             "%s %lX%08lX", | ||||
|             string_get_cstr(str_buff), | ||||
|             (uint32_t)(instance->history[idx].code_found >> 32), | ||||
|             (uint32_t)(instance->history[idx].code_found & 0xFFFFFFFF)); | ||||
|         string_clear(str_buff); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void subghz_history_add_to_history(SubGhzHistory* instance, void* context) { | ||||
|     furi_assert(instance); | ||||
|     furi_assert(context); | ||||
|     SubGhzProtocolCommon* protocol = context; | ||||
| 
 | ||||
|     if(instance->last_index_write >= SUBGHZ_HISTORY_MAX) return; | ||||
|     if((instance->code_last_found == (protocol->code_last_found & 0xFFFF0FFFFFFFFFFF)) && | ||||
|        ((millis() - instance->last_update_timestamp) < 500)) { | ||||
|         instance->last_update_timestamp = millis(); | ||||
|         return; | ||||
|     } | ||||
| 
 | ||||
|     instance->code_last_found = protocol->code_last_found & 0xFFFF0FFFFFFFFFFF; | ||||
|     instance->last_update_timestamp = millis(); | ||||
| 
 | ||||
|     instance->history[instance->last_index_write].te = 0; | ||||
|     instance->history[instance->last_index_write].manufacture_name = NULL; | ||||
|     instance->history[instance->last_index_write].name = protocol->name; | ||||
|     instance->history[instance->last_index_write].code_count_bit = protocol->code_last_count_bit; | ||||
|     instance->history[instance->last_index_write].code_found = protocol->code_last_found; | ||||
|     if(strcmp(protocol->name, "KeeLoq") == 0) { | ||||
|         instance->history[instance->last_index_write].manufacture_name = | ||||
|             subghz_protocol_keeloq_find_and_get_manufacture_name(protocol); | ||||
|     } else if(strcmp(protocol->name, "Star Line") == 0) { | ||||
|         instance->history[instance->last_index_write].manufacture_name = | ||||
|             subghz_protocol_star_line_find_and_get_manufacture_name(protocol); | ||||
|     } else if(strcmp(protocol->name, "Princeton") == 0) { | ||||
|         instance->history[instance->last_index_write].te = | ||||
|             subghz_protocol_princeton_get_te(protocol); | ||||
|     } | ||||
|     instance->history[instance->last_index_write].type_protocol = protocol->type_protocol; | ||||
| 
 | ||||
|     instance->last_index_write++; | ||||
| } | ||||
							
								
								
									
										106
									
								
								applications/subghz/subghz_history.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @ -0,0 +1,106 @@ | ||||
| #pragma once | ||||
| 
 | ||||
| #include <lib/subghz/protocols/subghz_protocol_common.h> | ||||
| 
 | ||||
| typedef struct SubGhzHistory SubGhzHistory; | ||||
| 
 | ||||
| /** Allocate SubGhzHistory
 | ||||
|  *  | ||||
|  * @return SubGhzHistory*  | ||||
|  */ | ||||
| SubGhzHistory* subghz_history_alloc(void); | ||||
| 
 | ||||
| /** Free SubGhzHistory
 | ||||
|  *  | ||||
|  * @param instance - SubGhzHistory instance | ||||
|  */ | ||||
| void subghz_history_free(SubGhzHistory* instance); | ||||
| 
 | ||||
| /** Clear history
 | ||||
|  *  | ||||
|  * @param instance - SubGhzHistory instance | ||||
|  */ | ||||
| void subghz_history_clean(SubGhzHistory* instance); | ||||
| 
 | ||||
| /** Set frequency and preset to history[idx]
 | ||||
|  *  | ||||
|  * @param instance  - SubGhzHistory instance | ||||
|  * @param idx       - record index   | ||||
|  * @param frequency - frequency Hz | ||||
|  * @param preset    - FuriHalSubGhzPreset preset | ||||
|  */ | ||||
| void subghz_history_set_frequency_preset( | ||||
|     SubGhzHistory* instance, | ||||
|     uint16_t idx, | ||||
|     uint32_t frequency, | ||||
|     FuriHalSubGhzPreset preset); | ||||
| 
 | ||||
| /** Get frequency to history[idx]
 | ||||
|  *  | ||||
|  * @param instance  - SubGhzHistory instance | ||||
|  * @param idx       - record index   | ||||
|  * @return frequency - frequency Hz | ||||
|  */ | ||||
| uint32_t subghz_history_get_frequency(SubGhzHistory* instance, uint16_t idx); | ||||
| 
 | ||||
| /** Get preset to history[idx]
 | ||||
|  *  | ||||
|  * @param instance  - SubGhzHistory instance | ||||
|  * @param idx       - record index   | ||||
|  * @return preset    - FuriHalSubGhzPreset preset | ||||
|  */ | ||||
| FuriHalSubGhzPreset subghz_history_get_preset(SubGhzHistory* instance, uint16_t idx); | ||||
| 
 | ||||
| /** Get history index write 
 | ||||
|  *  | ||||
|  * @param instance  - SubGhzHistory instance | ||||
|  * @return idx      - current record index   | ||||
|  */ | ||||
| uint16_t subghz_history_get_item(SubGhzHistory* instance); | ||||
| 
 | ||||
| /** Get type protocol to history[idx]
 | ||||
|  *  | ||||
|  * @param instance  - SubGhzHistory instance | ||||
|  * @param idx       - record index   | ||||
|  * @return type      - type protocol   | ||||
|  */ | ||||
| uint8_t subghz_history_get_type_protocol(SubGhzHistory* instance, uint16_t idx); | ||||
| 
 | ||||
| /** Get name protocol to history[idx]
 | ||||
|  *  | ||||
|  * @param instance  - SubGhzHistory instance | ||||
|  * @param idx       - record index   | ||||
|  * @return name      - const char* name protocol   | ||||
|  */ | ||||
| const char* subghz_history_get_name(SubGhzHistory* instance, uint16_t idx); | ||||
| 
 | ||||
| /** Get string item menu to history[idx]
 | ||||
|  *  | ||||
|  * @param instance  - SubGhzHistory instance | ||||
|  * @param output    - string_t output | ||||
|  * @param idx       - record index | ||||
|  */ | ||||
| void subghz_history_get_text_item_menu(SubGhzHistory* instance, string_t output, uint16_t idx); | ||||
| 
 | ||||
| /** Get string the remaining number of records to history
 | ||||
|  *  | ||||
|  * @param instance  - SubGhzHistory instance | ||||
|  * @param output    - string_t output | ||||
|  * @return bool - is FUUL | ||||
|  */ | ||||
| bool subghz_history_get_text_space_left(SubGhzHistory* instance, string_t output); | ||||
| 
 | ||||
| /** Add protocol to history
 | ||||
|  *  | ||||
|  * @param instance  - SubGhzHistory instance | ||||
|  * @param context    - SubGhzProtocolCommon context | ||||
|  */ | ||||
| void subghz_history_add_to_history(SubGhzHistory* instance, void* context); | ||||
| 
 | ||||
| /** Get SubGhzProtocolCommonLoad to load into the protocol decoder bin data
 | ||||
|  *  | ||||
|  * @param instance  - SubGhzHistory instance | ||||
|  * @param idx       - record index | ||||
|  * @return SubGhzProtocolCommonLoad* | ||||
|  */ | ||||
| SubGhzProtocolCommonLoad* subghz_history_get_raw_data(SubGhzHistory* instance, uint16_t idx); | ||||
| @ -8,6 +8,7 @@ | ||||
| #include <notification/notification-messages.h> | ||||
| #include "file-worker.h" | ||||
| #include "../notification/notification.h" | ||||
| #include "views/subghz_receiver.h" | ||||
| 
 | ||||
| void subghz_begin(FuriHalSubGhzPreset preset) { | ||||
|     furi_hal_subghz_reset(); | ||||
| @ -16,30 +17,59 @@ void subghz_begin(FuriHalSubGhzPreset preset) { | ||||
|     hal_gpio_init(&gpio_cc1101_g0, GpioModeInput, GpioPullNo, GpioSpeedLow); | ||||
| } | ||||
| 
 | ||||
| void subghz_rx(uint32_t frequency) { | ||||
| uint32_t subghz_rx(void* context, uint32_t frequency) { | ||||
|     furi_assert(context); | ||||
|     SubGhzWorker* worker = context; | ||||
| 
 | ||||
|     furi_hal_subghz_idle(); | ||||
|     furi_hal_subghz_set_frequency_and_path(frequency); | ||||
|     uint32_t value = furi_hal_subghz_set_frequency_and_path(frequency); | ||||
|     hal_gpio_init(&gpio_cc1101_g0, GpioModeInput, GpioPullNo, GpioSpeedLow); | ||||
|     furi_hal_subghz_flush_rx(); | ||||
|     furi_hal_subghz_rx(); | ||||
| 
 | ||||
|     furi_hal_subghz_start_async_rx(subghz_worker_rx_callback, worker); | ||||
|     subghz_worker_start(worker); | ||||
|     return value; | ||||
| } | ||||
| 
 | ||||
| void subghz_tx(uint32_t frequency) { | ||||
| uint32_t subghz_tx(uint32_t frequency) { | ||||
|     furi_hal_subghz_idle(); | ||||
|     furi_hal_subghz_set_frequency_and_path(frequency); | ||||
|     uint32_t value = furi_hal_subghz_set_frequency_and_path(frequency); | ||||
|     hal_gpio_init(&gpio_cc1101_g0, GpioModeOutputPushPull, GpioPullNo, GpioSpeedLow); | ||||
|     hal_gpio_write(&gpio_cc1101_g0, true); | ||||
|     furi_hal_subghz_tx(); | ||||
|     return value; | ||||
| } | ||||
| 
 | ||||
| void subghz_idle(void) { | ||||
|     furi_hal_subghz_idle(); | ||||
| } | ||||
| 
 | ||||
| void subghz_end(void) { | ||||
| void subghz_rx_end(void* context) { | ||||
|     furi_assert(context); | ||||
|     SubGhzWorker* worker = context; | ||||
| 
 | ||||
|     if(subghz_worker_is_running(worker)) { | ||||
|         subghz_worker_stop(worker); | ||||
|         furi_hal_subghz_stop_async_rx(); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void subghz_sleep(void) { | ||||
|     furi_hal_subghz_sleep(); | ||||
| } | ||||
| 
 | ||||
| void subghz_frequency_preset_to_str(void* context, string_t output) { | ||||
|     furi_assert(context); | ||||
|     SubGhz* subghz = context; | ||||
|     string_cat_printf( | ||||
|         output, | ||||
|         "Frequency: %d\n" | ||||
|         "Preset: %d\n", | ||||
|         (int)subghz->frequency, | ||||
|         (int)subghz->preset); | ||||
| } | ||||
| 
 | ||||
| void subghz_transmitter_tx_start(void* context) { | ||||
|     SubGhz* subghz = context; | ||||
|     subghz->encoder = subghz_protocol_encoder_common_alloc(); | ||||
| @ -47,8 +77,17 @@ void subghz_transmitter_tx_start(void* context) { | ||||
|     //get upload
 | ||||
|     if(subghz->protocol_result->get_upload_protocol) { | ||||
|         if(subghz->protocol_result->get_upload_protocol(subghz->protocol_result, subghz->encoder)) { | ||||
|             subghz_begin(FuriHalSubGhzPresetOokAsync); | ||||
|             if(subghz->preset) { | ||||
|                 subghz_begin(subghz->preset); | ||||
|             } else { | ||||
|                 subghz_begin(FuriHalSubGhzPresetOok650Async); | ||||
|             } | ||||
|             if(subghz->frequency) { | ||||
|                 subghz_tx(subghz->frequency); | ||||
|             } else { | ||||
|                 subghz_tx(433920000); | ||||
|             } | ||||
| 
 | ||||
|             //Start TX
 | ||||
|             furi_hal_subghz_start_async_tx(subghz_protocol_encoder_common_yield, subghz->encoder); | ||||
|         } | ||||
| @ -59,7 +98,6 @@ void subghz_transmitter_tx_stop(void* context) { | ||||
|     SubGhz* subghz = context; | ||||
|     //Stop TX
 | ||||
|     furi_hal_subghz_stop_async_tx(); | ||||
|     subghz_end(); | ||||
|     subghz_protocol_encoder_common_free(subghz->encoder); | ||||
|     //if protocol dynamic then we save the last upload
 | ||||
|     if(subghz->protocol_result->type_protocol == TYPE_PROTOCOL_DYNAMIC) { | ||||
| @ -79,12 +117,35 @@ bool subghz_key_load(SubGhz* subghz, const char* file_path) { | ||||
|     string_init_set_str(path, file_path); | ||||
|     string_t temp_str; | ||||
|     string_init(temp_str); | ||||
|     int res = 0; | ||||
|     int data = 0; | ||||
| 
 | ||||
|     do { | ||||
|         if(!file_worker_open(file_worker, string_get_cstr(path), FSAM_READ, FSOM_OPEN_EXISTING)) { | ||||
|             break; | ||||
|         } | ||||
|         // Read and parse name protocol from 1st line
 | ||||
| 
 | ||||
|         // Read and parse frequency from 1st line
 | ||||
|         if(!file_worker_read_until(file_worker, temp_str, '\n')) { | ||||
|             break; | ||||
|         } | ||||
|         res = sscanf(string_get_cstr(temp_str), "Frequency: %d\n", &data); | ||||
|         if(res != 1) { | ||||
|             break; | ||||
|         } | ||||
|         subghz->frequency = (uint32_t)data; | ||||
| 
 | ||||
|         // Read and parse preset from 2st line
 | ||||
|         if(!file_worker_read_until(file_worker, temp_str, '\n')) { | ||||
|             break; | ||||
|         } | ||||
|         res = sscanf(string_get_cstr(temp_str), "Preset: %d\n", &data); | ||||
|         if(res != 1) { | ||||
|             break; | ||||
|         } | ||||
|         subghz->preset = (FuriHalSubGhzPreset)data; | ||||
| 
 | ||||
|         // Read and parse name protocol from 2st line
 | ||||
|         if(!file_worker_read_until(file_worker, temp_str, '\n')) { | ||||
|             break; | ||||
|         } | ||||
| @ -93,16 +154,18 @@ bool subghz_key_load(SubGhz* subghz, const char* file_path) { | ||||
|         subghz->protocol_result = | ||||
|             subghz_protocol_get_by_name(subghz->protocol, string_get_cstr(temp_str)); | ||||
|         if(subghz->protocol_result == NULL) { | ||||
|             file_worker_show_error(file_worker, "Cannot parse\nfile"); | ||||
|             break; | ||||
|         } | ||||
|         if(!subghz->protocol_result->to_load_protocol(file_worker, subghz->protocol_result)) { | ||||
|             file_worker_show_error(file_worker, "Cannot parse\nfile"); | ||||
|         if(!subghz->protocol_result->to_load_protocol_from_file( | ||||
|                file_worker, subghz->protocol_result)) { | ||||
|             break; | ||||
|         } | ||||
|         loaded = true; | ||||
|     } while(0); | ||||
| 
 | ||||
|     if(!loaded) { | ||||
|         file_worker_show_error(file_worker, "Cannot parse\nfile"); | ||||
|     } | ||||
|     string_clear(temp_str); | ||||
|     string_clear(path); | ||||
|     file_worker_close(file_worker); | ||||
| @ -113,6 +176,7 @@ bool subghz_key_load(SubGhz* subghz, const char* file_path) { | ||||
| 
 | ||||
| bool subghz_save_protocol_to_file(void* context, const char* dev_name) { | ||||
|     SubGhz* subghz = context; | ||||
|     furi_assert(subghz->protocol_result); | ||||
|     FileWorker* file_worker = file_worker_alloc(false); | ||||
|     string_t dev_file_name; | ||||
|     string_init(dev_file_name); | ||||
| @ -140,6 +204,11 @@ bool subghz_save_protocol_to_file(void* context, const char* dev_name) { | ||||
|                file_worker, string_get_cstr(dev_file_name), FSAM_WRITE, FSOM_CREATE_ALWAYS)) { | ||||
|             break; | ||||
|         } | ||||
|         //Get string frequency preset protocol
 | ||||
|         subghz_frequency_preset_to_str(subghz, temp_str); | ||||
|         if(!file_worker_write(file_worker, string_get_cstr(temp_str), string_size(temp_str))) { | ||||
|             break; | ||||
|         } | ||||
|         //Get string save
 | ||||
|         subghz->protocol_result->to_save_string(subghz->protocol_result, temp_str); | ||||
|         // Prepare and write data to file
 | ||||
| @ -157,7 +226,7 @@ bool subghz_save_protocol_to_file(void* context, const char* dev_name) { | ||||
|     return saved; | ||||
| } | ||||
| 
 | ||||
| bool subghz_saved_protocol_select(SubGhz* subghz) { | ||||
| bool subghz_load_protocol_from_file(SubGhz* subghz) { | ||||
|     furi_assert(subghz); | ||||
| 
 | ||||
|     FileWorker* file_worker = file_worker_alloc(false); | ||||
| @ -165,6 +234,8 @@ bool subghz_saved_protocol_select(SubGhz* subghz) { | ||||
|     string_init(protocol_file_name); | ||||
|     string_t temp_str; | ||||
|     string_init(temp_str); | ||||
|     int sscanf_res = 0; | ||||
|     int data = 0; | ||||
| 
 | ||||
|     // Input events and views are managed by file_select
 | ||||
|     bool res = file_worker_file_select( | ||||
| @ -197,7 +268,27 @@ bool subghz_saved_protocol_select(SubGhz* subghz) { | ||||
|                file_worker, string_get_cstr(protocol_file_name), FSAM_READ, FSOM_OPEN_EXISTING)) { | ||||
|             break; | ||||
|         } | ||||
|         // Read and parse name protocol from 1st line
 | ||||
|         // Read and parse frequency from 1st line
 | ||||
|         if(!file_worker_read_until(file_worker, temp_str, '\n')) { | ||||
|             break; | ||||
|         } | ||||
|         sscanf_res = sscanf(string_get_cstr(temp_str), "Frequency: %d\n", &data); | ||||
|         if(sscanf_res != 1) { | ||||
|             break; | ||||
|         } | ||||
|         subghz->frequency = (uint32_t)data; | ||||
| 
 | ||||
|         // Read and parse preset from 2st line
 | ||||
|         if(!file_worker_read_until(file_worker, temp_str, '\n')) { | ||||
|             break; | ||||
|         } | ||||
|         sscanf_res = sscanf(string_get_cstr(temp_str), "Preset: %d\n", &data); | ||||
|         if(sscanf_res != 1) { | ||||
|             break; | ||||
|         } | ||||
|         subghz->preset = (FuriHalSubGhzPreset)data; | ||||
| 
 | ||||
|         // Read and parse name protocol from 3st line
 | ||||
|         if(!file_worker_read_until(file_worker, temp_str, '\n')) { | ||||
|             break; | ||||
|         } | ||||
| @ -206,16 +297,19 @@ bool subghz_saved_protocol_select(SubGhz* subghz) { | ||||
|         subghz->protocol_result = | ||||
|             subghz_protocol_get_by_name(subghz->protocol, string_get_cstr(temp_str)); | ||||
|         if(subghz->protocol_result == NULL) { | ||||
|             file_worker_show_error(file_worker, "Cannot parse\nfile"); | ||||
|             break; | ||||
|         } | ||||
|         if(!subghz->protocol_result->to_load_protocol(file_worker, subghz->protocol_result)) { | ||||
|             file_worker_show_error(file_worker, "Cannot parse\nfile"); | ||||
|         if(!subghz->protocol_result->to_load_protocol_from_file( | ||||
|                file_worker, subghz->protocol_result)) { | ||||
|             break; | ||||
|         } | ||||
|         res = true; | ||||
|     } while(0); | ||||
| 
 | ||||
|     if(!res) { | ||||
|         file_worker_show_error(file_worker, "Cannot parse\nfile"); | ||||
|     } | ||||
| 
 | ||||
|     string_clear(temp_str); | ||||
|     string_clear(protocol_file_name); | ||||
| 
 | ||||
|  | ||||
| @ -1,11 +1,10 @@ | ||||
| #pragma once | ||||
| 
 | ||||
| #include "subghz.h" | ||||
| #include "views/subghz_analyze.h" | ||||
| #include "views/subghz_receiver.h" | ||||
| #include "views/subghz_transmitter.h" | ||||
| #include "views/subghz_static.h" | ||||
| 
 | ||||
| #include "views/subghz_test_static.h" | ||||
| #include "views/subghz_test_carrier.h" | ||||
| #include "views/subghz_test_packet.h" | ||||
| 
 | ||||
| @ -25,12 +24,14 @@ | ||||
| #include <lib/subghz/subghz_worker.h> | ||||
| #include <lib/subghz/protocols/subghz_protocol.h> | ||||
| #include <lib/subghz/protocols/subghz_protocol_common.h> | ||||
| #include "subghz_history.h" | ||||
| 
 | ||||
| #define SUBGHZ_TEXT_STORE_SIZE 128 | ||||
| 
 | ||||
| #define NOTIFICATION_STARTING_STATE 0u | ||||
| #define NOTIFICATION_IDLE_STATE 1u | ||||
| #define NOTIFICATION_TX_STATE 2u | ||||
| #define NOTIFICATION_RX_STATE 3u | ||||
| 
 | ||||
| extern const uint32_t subghz_frequencies[]; | ||||
| extern const uint32_t subghz_frequencies_count; | ||||
| @ -43,10 +44,11 @@ struct SubGhz { | ||||
|     SubGhzWorker* worker; | ||||
|     SubGhzProtocol* protocol; | ||||
|     SubGhzProtocolCommon* protocol_result; | ||||
|     SubGhzProtocolEncoderCommon* encoder; | ||||
|     SubGhzProtocolCommonEncoder* encoder; | ||||
|     uint32_t frequency; | ||||
|     FuriHalSubGhzPreset preset; | ||||
| 
 | ||||
|     SceneManager* scene_manager; | ||||
| 
 | ||||
|     ViewDispatcher* view_dispatcher; | ||||
| 
 | ||||
|     Submenu* submenu; | ||||
| @ -56,11 +58,10 @@ struct SubGhz { | ||||
|     char text_store[SUBGHZ_TEXT_STORE_SIZE + 1]; | ||||
|     uint8_t state_notifications; | ||||
| 
 | ||||
|     SubghzAnalyze* subghz_analyze; | ||||
|     SubghzReceiver* subghz_receiver; | ||||
|     SubghzTransmitter* subghz_transmitter; | ||||
|     SubghzStatic* subghz_static; | ||||
| 
 | ||||
|     SubghzTestStatic* subghz_test_static; | ||||
|     SubghzTestCarrier* subghz_test_carrier; | ||||
|     SubghzTestPacket* subghz_test_packet; | ||||
| }; | ||||
| @ -68,7 +69,6 @@ struct SubGhz { | ||||
| typedef enum { | ||||
|     SubGhzViewMenu, | ||||
| 
 | ||||
|     SubGhzViewAnalyze, | ||||
|     SubGhzViewDialogEx, | ||||
|     SubGhzViewReceiver, | ||||
|     SubGhzViewPopup, | ||||
| @ -81,13 +81,14 @@ typedef enum { | ||||
| } SubGhzView; | ||||
| 
 | ||||
| void subghz_begin(FuriHalSubGhzPreset preset); | ||||
| void subghz_rx(uint32_t frequency); | ||||
| void subghz_tx(uint32_t frequency); | ||||
| uint32_t subghz_rx(void* context, uint32_t frequency); | ||||
| uint32_t subghz_tx(uint32_t frequency); | ||||
| void subghz_idle(void); | ||||
| void subghz_end(void); | ||||
| void subghz_rx_end(void* context); | ||||
| void subghz_sleep(void); | ||||
| void subghz_transmitter_tx_start(void* context); | ||||
| void subghz_transmitter_tx_stop(void* context); | ||||
| bool subghz_key_load(SubGhz* subghz, const char* file_path); | ||||
| bool subghz_save_protocol_to_file(void* context, const char* dev_name); | ||||
| bool subghz_saved_protocol_select(SubGhz* subghz); | ||||
| bool subghz_load_protocol_from_file(SubGhz* subghz); | ||||
| uint32_t subghz_random_serial(void); | ||||
|  | ||||
| @ -1,233 +0,0 @@ | ||||
| #include "subghz_analyze.h" | ||||
| #include "../subghz_i.h" | ||||
| 
 | ||||
| #include <math.h> | ||||
| #include <furi.h> | ||||
| #include <furi-hal.h> | ||||
| #include <input/input.h> | ||||
| #include <gui/elements.h> | ||||
| #include <notification/notification-messages.h> | ||||
| 
 | ||||
| #include <lib/subghz/subghz_worker.h> | ||||
| #include <lib/subghz/protocols/subghz_protocol.h> | ||||
| 
 | ||||
| #include <assets_icons.h> | ||||
| 
 | ||||
| struct SubghzAnalyze { | ||||
|     View* view; | ||||
|     SubGhzWorker* worker; | ||||
|     SubGhzProtocol* protocol; | ||||
| }; | ||||
| 
 | ||||
| typedef struct { | ||||
|     uint8_t frequency; | ||||
|     uint32_t real_frequency; | ||||
|     uint32_t counter; | ||||
|     string_t text; | ||||
|     uint16_t scene; | ||||
|     SubGhzProtocolCommon parser; | ||||
| } SubghzAnalyzeModel; | ||||
| 
 | ||||
| static const char subghz_symbols[] = {'-', '\\', '|', '/'}; | ||||
| 
 | ||||
| void subghz_analyze_draw(Canvas* canvas, SubghzAnalyzeModel* model) { | ||||
|     char buffer[64]; | ||||
| 
 | ||||
|     canvas_set_color(canvas, ColorBlack); | ||||
|     canvas_set_font(canvas, FontPrimary); | ||||
| 
 | ||||
|     snprintf( | ||||
|         buffer, | ||||
|         sizeof(buffer), | ||||
|         "Analyze: %03ld.%03ldMHz %c", | ||||
|         model->real_frequency / 1000000 % 1000, | ||||
|         model->real_frequency / 1000 % 1000, | ||||
|         subghz_symbols[model->counter % 4]); | ||||
|     canvas_draw_str(canvas, 0, 8, buffer); | ||||
| 
 | ||||
|     switch(model->scene) { | ||||
|     case 1: | ||||
|         canvas_draw_icon(canvas, 0, 10, &I_RFIDDolphinReceive_97x61); | ||||
|         canvas_invert_color(canvas); | ||||
|         canvas_draw_box(canvas, 80, 12, 20, 20); | ||||
|         canvas_invert_color(canvas); | ||||
|         canvas_draw_icon(canvas, 75, 18, &I_sub1_10px); | ||||
|         elements_multiline_text_aligned( | ||||
|             canvas, 90, 38, AlignCenter, AlignTop, "Detecting\r\nSubGhz"); | ||||
|         break; | ||||
| 
 | ||||
|     default: | ||||
|         canvas_set_font(canvas, FontSecondary); | ||||
|         elements_multiline_text(canvas, 0, 20, string_get_cstr(model->text)); | ||||
|         break; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| bool subghz_analyze_input(InputEvent* event, void* context) { | ||||
|     furi_assert(context); | ||||
|     SubghzAnalyze* subghz_analyze = context; | ||||
| 
 | ||||
|     if(event->type != InputTypeShort) return false; | ||||
| 
 | ||||
|     if(event->key == InputKeyBack) { | ||||
|         return false; | ||||
|     } | ||||
| 
 | ||||
|     with_view_model( | ||||
|         subghz_analyze->view, (SubghzAnalyzeModel * model) { | ||||
|             bool model_updated = false; | ||||
| 
 | ||||
|             if(event->key == InputKeyLeft) { | ||||
|                 if(model->frequency > 0) model->frequency--; | ||||
|                 model_updated = true; | ||||
|             } else if(event->key == InputKeyRight) { | ||||
|                 if(model->frequency < subghz_frequencies_count - 1) model->frequency++; | ||||
|                 model_updated = true; | ||||
|             } | ||||
| 
 | ||||
|             if(model_updated) { | ||||
|                 furi_hal_subghz_idle(); | ||||
|                 model->real_frequency = | ||||
|                     furi_hal_subghz_set_frequency_and_path(subghz_frequencies[model->frequency]); | ||||
|                 furi_hal_subghz_rx(); | ||||
|             } | ||||
| 
 | ||||
|             return model_updated; | ||||
|         }); | ||||
| 
 | ||||
|     return true; | ||||
| } | ||||
| 
 | ||||
| void subghz_analyze_text_callback(string_t text, void* context) { | ||||
|     furi_assert(context); | ||||
|     SubghzAnalyze* subghz_analyze = context; | ||||
| 
 | ||||
|     with_view_model( | ||||
|         subghz_analyze->view, (SubghzAnalyzeModel * model) { | ||||
|             model->counter++; | ||||
|             string_set(model->text, text); | ||||
|             model->scene = 0; | ||||
|             return true; | ||||
|         }); | ||||
| } | ||||
| 
 | ||||
| void subghz_analyze_protocol_callback(SubGhzProtocolCommon* parser, void* context) { | ||||
|     furi_assert(context); | ||||
|     SubghzAnalyze* subghz_analyze = context; | ||||
|     char buffer[64]; | ||||
|     snprintf( | ||||
|         buffer, | ||||
|         sizeof(buffer), | ||||
|         "%s\r\n" | ||||
|         "K:%lX%lX\r\n" | ||||
|         "SN:%lX\r\n" | ||||
|         "BTN:%X", | ||||
|         parser->name, | ||||
|         (uint32_t)(parser->code_found >> 32), | ||||
|         (uint32_t)(parser->code_found & 0x00000000FFFFFFFF), | ||||
|         parser->serial, | ||||
|         parser->btn); | ||||
| 
 | ||||
|     with_view_model( | ||||
|         subghz_analyze->view, (SubghzAnalyzeModel * model) { | ||||
|             model->counter++; | ||||
|             model->parser = *parser; | ||||
|             string_set(model->text, buffer); | ||||
|             model->scene = 0; | ||||
|             return true; | ||||
|         }); | ||||
| } | ||||
| 
 | ||||
| void subghz_analyze_enter(void* context) { | ||||
|     furi_assert(context); | ||||
|     SubghzAnalyze* subghz_analyze = context; | ||||
| 
 | ||||
|     furi_hal_subghz_reset(); | ||||
|     furi_hal_subghz_idle(); | ||||
|     furi_hal_subghz_load_preset(FuriHalSubGhzPresetOokAsync); | ||||
| 
 | ||||
|     with_view_model( | ||||
|         subghz_analyze->view, (SubghzAnalyzeModel * model) { | ||||
|             model->frequency = subghz_frequencies_433_92; | ||||
|             model->real_frequency = | ||||
|                 furi_hal_subghz_set_frequency_and_path(subghz_frequencies[model->frequency]); | ||||
|             model->scene = 1; | ||||
|             return true; | ||||
|         }); | ||||
| 
 | ||||
|     hal_gpio_init(&gpio_cc1101_g0, GpioModeInput, GpioPullNo, GpioSpeedLow); | ||||
| 
 | ||||
|     furi_hal_subghz_start_async_rx(subghz_worker_rx_callback, subghz_analyze->worker); | ||||
| 
 | ||||
|     subghz_worker_start(subghz_analyze->worker); | ||||
| 
 | ||||
|     furi_hal_subghz_flush_rx(); | ||||
|     furi_hal_subghz_rx(); | ||||
| } | ||||
| 
 | ||||
| void subghz_analyze_exit(void* context) { | ||||
|     furi_assert(context); | ||||
|     SubghzAnalyze* subghz_analyze = context; | ||||
| 
 | ||||
|     subghz_worker_stop(subghz_analyze->worker); | ||||
| 
 | ||||
|     furi_hal_subghz_stop_async_rx(); | ||||
|     furi_hal_subghz_sleep(); | ||||
| } | ||||
| 
 | ||||
| SubghzAnalyze* subghz_analyze_alloc() { | ||||
|     SubghzAnalyze* subghz_analyze = furi_alloc(sizeof(SubghzAnalyze)); | ||||
| 
 | ||||
|     // View allocation and configuration
 | ||||
|     subghz_analyze->view = view_alloc(); | ||||
|     view_allocate_model(subghz_analyze->view, ViewModelTypeLocking, sizeof(SubghzAnalyzeModel)); | ||||
|     view_set_context(subghz_analyze->view, subghz_analyze); | ||||
|     view_set_draw_callback(subghz_analyze->view, (ViewDrawCallback)subghz_analyze_draw); | ||||
|     view_set_input_callback(subghz_analyze->view, subghz_analyze_input); | ||||
|     view_set_enter_callback(subghz_analyze->view, subghz_analyze_enter); | ||||
|     view_set_exit_callback(subghz_analyze->view, subghz_analyze_exit); | ||||
| 
 | ||||
|     with_view_model( | ||||
|         subghz_analyze->view, (SubghzAnalyzeModel * model) { | ||||
|             string_init(model->text); | ||||
|             return true; | ||||
|         }); | ||||
| 
 | ||||
|     subghz_analyze->worker = subghz_worker_alloc(); | ||||
|     subghz_analyze->protocol = subghz_protocol_alloc(); | ||||
| 
 | ||||
|     subghz_worker_set_overrun_callback( | ||||
|         subghz_analyze->worker, (SubGhzWorkerOverrunCallback)subghz_protocol_reset); | ||||
|     subghz_worker_set_pair_callback( | ||||
|         subghz_analyze->worker, (SubGhzWorkerPairCallback)subghz_protocol_parse); | ||||
|     subghz_worker_set_context(subghz_analyze->worker, subghz_analyze->protocol); | ||||
| 
 | ||||
|     subghz_protocol_load_keeloq_file( | ||||
|         subghz_analyze->protocol, "/ext/assets/subghz/keeloq_mfcodes"); | ||||
|     subghz_protocol_load_nice_flor_s_file( | ||||
|         subghz_analyze->protocol, "/ext/assets/subghz/nice_floor_s_rx"); | ||||
|     subghz_protocol_enable_dump_text( | ||||
|         subghz_analyze->protocol, subghz_analyze_text_callback, subghz_analyze); | ||||
| 
 | ||||
|     return subghz_analyze; | ||||
| } | ||||
| 
 | ||||
| void subghz_analyze_free(SubghzAnalyze* subghz_analyze) { | ||||
|     furi_assert(subghz_analyze); | ||||
| 
 | ||||
|     subghz_protocol_free(subghz_analyze->protocol); | ||||
|     subghz_worker_free(subghz_analyze->worker); | ||||
| 
 | ||||
|     with_view_model( | ||||
|         subghz_analyze->view, (SubghzAnalyzeModel * model) { | ||||
|             string_clear(model->text); | ||||
|             return true; | ||||
|         }); | ||||
|     view_free(subghz_analyze->view); | ||||
|     free(subghz_analyze); | ||||
| } | ||||
| 
 | ||||
| View* subghz_analyze_get_view(SubghzAnalyze* subghz_analyze) { | ||||
|     furi_assert(subghz_analyze); | ||||
|     return subghz_analyze->view; | ||||
| } | ||||
| @ -1,11 +0,0 @@ | ||||
| #pragma once | ||||
| 
 | ||||
| #include <gui/view.h> | ||||
| 
 | ||||
| typedef struct SubghzAnalyze SubghzAnalyze; | ||||
| 
 | ||||
| SubghzAnalyze* subghz_analyze_alloc(); | ||||
| 
 | ||||
| void subghz_analyze_free(SubghzAnalyze* subghz_analyze); | ||||
| 
 | ||||
| View* subghz_analyze_get_view(SubghzAnalyze* subghz_analyze); | ||||
| @ -1,25 +1,74 @@ | ||||
| #include "subghz_receiver.h" | ||||
| #include "../subghz_i.h" | ||||
| 
 | ||||
| #include <math.h> | ||||
| #include <furi.h> | ||||
| #include <furi-hal.h> | ||||
| #include <input/input.h> | ||||
| #include <gui/elements.h> | ||||
| #include <notification/notification-messages.h> | ||||
| #include <lib/subghz/protocols/subghz_protocol_princeton.h> | ||||
| 
 | ||||
| #include <assets_icons.h> | ||||
| 
 | ||||
| #define FRAME_HEIGHT 12 | ||||
| #define MAX_LEN_PX 100 | ||||
| #define MENU_ITEMS 4 | ||||
| 
 | ||||
| #define COUNT_FREQUNCY_HOPPER 3 | ||||
| const uint32_t subghz_frequencies_hopper[] = { | ||||
|     /* 300 - 348 */ | ||||
|     315000000, | ||||
|     /* 387 - 464 */ | ||||
|     433920000, /* LPD433 mid */ | ||||
|     /* 779 - 928 */ | ||||
|     868350000, | ||||
| }; | ||||
| 
 | ||||
| typedef enum { | ||||
|     ReceiverSceneStart, | ||||
|     ReceiverSceneMain, | ||||
|     ReceiverSceneConfig, | ||||
|     ReceiverSceneInfo, | ||||
| } SubghzReceiverScene; | ||||
| 
 | ||||
| typedef enum { | ||||
|     SubGhzHopperStateOFF, | ||||
|     SubGhzHopperStatePause, | ||||
|     SubGhzHopperStateRunnig, | ||||
|     SubGhzHopperStateRSSITimeOut, | ||||
| } SubGhzHopperState; | ||||
| 
 | ||||
| static const Icon* ReceiverItemIcons[] = { | ||||
|     [TYPE_PROTOCOL_UNKNOWN] = &I_Quest_7x8, | ||||
|     [TYPE_PROTOCOL_STATIC] = &I_Unlock_7x8, | ||||
|     [TYPE_PROTOCOL_DYNAMIC] = &I_Lock_7x8, | ||||
| }; | ||||
| 
 | ||||
| struct SubghzReceiver { | ||||
|     View* view; | ||||
|     SubghzReceiverCallback callback; | ||||
|     void* context; | ||||
|     SubGhzWorker* worker; | ||||
|     SubGhzProtocol* protocol; | ||||
|     osTimerId timer; | ||||
|     SubGhzHopperState hopper_state; | ||||
|     uint8_t hopper_timeout; | ||||
|     uint32_t event_key_sequence; | ||||
| }; | ||||
| 
 | ||||
| typedef struct { | ||||
|     string_t text; | ||||
|     uint16_t scene; | ||||
|     SubGhzProtocolCommon* protocol; | ||||
|     SubGhzProtocolCommon* protocol_result; | ||||
|     SubGhzHistory* history; | ||||
|     uint8_t frequency; | ||||
|     uint8_t temp_frequency; | ||||
|     uint32_t real_frequency; | ||||
| 
 | ||||
|     uint16_t idx; | ||||
|     uint16_t list_offset; | ||||
|     uint16_t history_item; | ||||
|     bool menu; | ||||
| } SubghzReceiverModel; | ||||
| 
 | ||||
| void subghz_receiver_set_callback( | ||||
| @ -32,48 +81,393 @@ void subghz_receiver_set_callback( | ||||
|     subghz_receiver->context = context; | ||||
| } | ||||
| 
 | ||||
| void subghz_receiver_set_protocol(SubghzReceiver* subghz_receiver, SubGhzProtocolCommon* protocol) { | ||||
| void subghz_receiver_set_protocol( | ||||
|     SubghzReceiver* subghz_receiver, | ||||
|     SubGhzProtocolCommon* protocol_result, | ||||
|     SubGhzProtocol* protocol) { | ||||
|     furi_assert(subghz_receiver); | ||||
|     with_view_model( | ||||
|         subghz_receiver->view, (SubghzReceiverModel * model) { | ||||
|             model->protocol = protocol; | ||||
|             model->protocol_result = protocol_result; | ||||
|             return true; | ||||
|         }); | ||||
|     subghz_receiver->protocol = protocol; | ||||
| } | ||||
| 
 | ||||
| SubGhzProtocolCommon* subghz_receiver_get_protocol(SubghzReceiver* subghz_receiver) { | ||||
|     furi_assert(subghz_receiver); | ||||
|     SubGhzProtocolCommon* result = NULL; | ||||
|     with_view_model( | ||||
|         subghz_receiver->view, (SubghzReceiverModel * model) { | ||||
|             result = model->protocol_result; | ||||
|             return false; | ||||
|         }); | ||||
|     return result; | ||||
| } | ||||
| 
 | ||||
| void subghz_receiver_set_worker(SubghzReceiver* subghz_receiver, SubGhzWorker* worker) { | ||||
|     furi_assert(subghz_receiver); | ||||
|     subghz_receiver->worker = worker; | ||||
| } | ||||
| 
 | ||||
| static void subghz_receiver_update_offset(SubghzReceiver* subghz_receiver) { | ||||
|     furi_assert(subghz_receiver); | ||||
| 
 | ||||
|     with_view_model( | ||||
|         subghz_receiver->view, (SubghzReceiverModel * model) { | ||||
|             size_t history_item = model->history_item; | ||||
|             uint16_t bounds = history_item > 3 ? 2 : history_item; | ||||
| 
 | ||||
|             if(history_item > 3 && model->idx >= history_item - 1) { | ||||
|                 model->list_offset = model->idx - 3; | ||||
|             } else if(model->list_offset < model->idx - bounds) { | ||||
|                 model->list_offset = CLAMP(model->list_offset + 1, history_item - bounds, 0); | ||||
|             } else if(model->list_offset > model->idx - bounds) { | ||||
|                 model->list_offset = CLAMP(model->idx - 1, history_item - bounds, 0); | ||||
|             } | ||||
|             return true; | ||||
|         }); | ||||
| } | ||||
| 
 | ||||
| static void subghz_receiver_draw_frame(Canvas* canvas, uint16_t idx, bool scrollbar) { | ||||
|     canvas_set_color(canvas, ColorBlack); | ||||
|     canvas_draw_box(canvas, 0, 0 + idx * FRAME_HEIGHT, scrollbar ? 122 : 127, FRAME_HEIGHT); | ||||
| 
 | ||||
|     canvas_set_color(canvas, ColorWhite); | ||||
|     canvas_draw_dot(canvas, 0, 0 + idx * FRAME_HEIGHT); | ||||
|     canvas_draw_dot(canvas, 1, 0 + idx * FRAME_HEIGHT); | ||||
|     canvas_draw_dot(canvas, 0, (0 + idx * FRAME_HEIGHT) + 1); | ||||
| 
 | ||||
|     canvas_draw_dot(canvas, 0, (0 + idx * FRAME_HEIGHT) + 11); | ||||
|     canvas_draw_dot(canvas, scrollbar ? 121 : 126, 0 + idx * FRAME_HEIGHT); | ||||
|     canvas_draw_dot(canvas, scrollbar ? 121 : 126, (0 + idx * FRAME_HEIGHT) + 11); | ||||
| } | ||||
| 
 | ||||
| void subghz_receiver_draw(Canvas* canvas, SubghzReceiverModel* model) { | ||||
|     bool scrollbar = model->history_item > 4; | ||||
|     string_t str_buff; | ||||
|     char buffer[64]; | ||||
|     uint32_t frequency; | ||||
|     string_init(str_buff); | ||||
| 
 | ||||
|     canvas_clear(canvas); | ||||
|     canvas_set_color(canvas, ColorBlack); | ||||
|     canvas_set_font(canvas, FontSecondary); | ||||
|     elements_multiline_text(canvas, 0, 10, string_get_cstr(model->text)); | ||||
| 
 | ||||
|     elements_button_left(canvas, "Back"); | ||||
|     if(model->protocol && model->protocol->to_save_string && | ||||
|        strcmp(model->protocol->name, "KeeLoq")) { | ||||
|         elements_button_right(canvas, "Save"); | ||||
|     switch(model->scene) { | ||||
|     case ReceiverSceneMain: | ||||
|         for(size_t i = 0; i < MIN(model->history_item, MENU_ITEMS); ++i) { | ||||
|             size_t idx = CLAMP(i + model->list_offset, model->history_item, 0); | ||||
|             subghz_history_get_text_item_menu(model->history, str_buff, idx); | ||||
|             elements_string_fit_width(canvas, str_buff, scrollbar ? MAX_LEN_PX - 6 : MAX_LEN_PX); | ||||
|             if(model->idx == idx) { | ||||
|                 subghz_receiver_draw_frame(canvas, i, scrollbar); | ||||
|             } else { | ||||
|                 canvas_set_color(canvas, ColorBlack); | ||||
|             } | ||||
|             canvas_draw_icon( | ||||
|                 canvas, | ||||
|                 1, | ||||
|                 2 + i * FRAME_HEIGHT, | ||||
|                 ReceiverItemIcons[subghz_history_get_type_protocol(model->history, idx)]); | ||||
|             canvas_draw_str(canvas, 15, 9 + i * FRAME_HEIGHT, string_get_cstr(str_buff)); | ||||
|             string_clean(str_buff); | ||||
|         } | ||||
|         if(scrollbar) { | ||||
|             elements_scrollbar_pos(canvas, 128, 0, 49, model->idx, model->history_item); | ||||
|         } | ||||
|         canvas_set_color(canvas, ColorBlack); | ||||
|         canvas_set_font(canvas, FontSecondary); | ||||
| 
 | ||||
|         elements_button_left(canvas, "Config"); | ||||
|         canvas_draw_line(canvas, 46, 51, 125, 51); | ||||
|         if(subghz_history_get_text_space_left(model->history, str_buff)) { | ||||
|             canvas_draw_str(canvas, 54, 62, string_get_cstr(str_buff)); | ||||
|         } else { | ||||
|             if((model->real_frequency / 1000 % 10) > 4) { | ||||
|                 frequency = model->real_frequency + 10000; | ||||
|             } else { | ||||
|                 frequency = model->real_frequency; | ||||
|             } | ||||
|             snprintf( | ||||
|                 buffer, | ||||
|                 sizeof(buffer), | ||||
|                 "%03ld.%02ld", | ||||
|                 frequency / 1000000 % 1000, | ||||
|                 frequency / 10000 % 100); | ||||
|             canvas_draw_str(canvas, 44, 62, buffer); | ||||
|             canvas_draw_str(canvas, 79, 62, "AM"); | ||||
|             canvas_draw_str(canvas, 96, 62, string_get_cstr(str_buff)); | ||||
|         } | ||||
|         break; | ||||
| 
 | ||||
|     case ReceiverSceneStart: | ||||
|         canvas_draw_icon(canvas, 0, 0, &I_Scanning_123x52); | ||||
|         canvas_set_font(canvas, FontPrimary); | ||||
|         canvas_draw_str(canvas, 63, 46, "Scanning..."); | ||||
|         canvas_set_color(canvas, ColorBlack); | ||||
|         canvas_set_font(canvas, FontSecondary); | ||||
|         elements_button_left(canvas, "Config"); | ||||
|         if((model->real_frequency / 1000 % 10) > 4) { | ||||
|             frequency = model->real_frequency + 10000; | ||||
|         } else { | ||||
|             frequency = model->real_frequency; | ||||
|         } | ||||
|         snprintf( | ||||
|             buffer, | ||||
|             sizeof(buffer), | ||||
|             "%03ld.%02ld", | ||||
|             frequency / 1000000 % 1000, | ||||
|             frequency / 10000 % 100); | ||||
|         canvas_draw_str(canvas, 44, 62, buffer); | ||||
|         canvas_draw_str(canvas, 79, 62, "AM"); | ||||
|         subghz_history_get_text_space_left(model->history, str_buff); | ||||
|         canvas_draw_str(canvas, 96, 62, string_get_cstr(str_buff)); | ||||
|         canvas_draw_line(canvas, 46, 51, 125, 51); | ||||
|         break; | ||||
| 
 | ||||
|     case ReceiverSceneConfig: | ||||
|         if(model->frequency < subghz_frequencies_count) { | ||||
|             snprintf( | ||||
|                 buffer, | ||||
|                 sizeof(buffer), | ||||
|                 "Frequency:  < %03ld.%03ldMHz >", | ||||
|                 model->real_frequency / 1000000 % 1000, | ||||
|                 model->real_frequency / 1000 % 1000); | ||||
|             canvas_draw_str(canvas, 0, 8, buffer); | ||||
|             canvas_draw_str(canvas, 0, 18, "Frequency Hopping: <OFF>"); | ||||
|         } else { | ||||
|             canvas_draw_str(canvas, 0, 8, "Frequency: < --- >"); | ||||
|             canvas_draw_str(canvas, 0, 18, "Frequency Hopping: <ON>"); | ||||
|         } | ||||
|         canvas_draw_str(canvas, 0, 28, "Modulation: <AM>"); | ||||
| 
 | ||||
|         elements_button_center(canvas, "Save"); | ||||
|         break; | ||||
| 
 | ||||
|     case ReceiverSceneInfo: | ||||
|         canvas_set_font(canvas, FontSecondary); | ||||
|         elements_multiline_text(canvas, 0, 8, string_get_cstr(model->text)); | ||||
|         snprintf( | ||||
|             buffer, | ||||
|             sizeof(buffer), | ||||
|             "%03ld.%03ld", | ||||
|             subghz_history_get_frequency(model->history, model->idx) / 1000000 % 1000, | ||||
|             subghz_history_get_frequency(model->history, model->idx) / 1000 % 1000); | ||||
|         canvas_draw_str(canvas, 90, 8, buffer); | ||||
|         if(model->protocol_result && model->protocol_result->to_save_string && | ||||
|            strcmp(model->protocol_result->name, "KeeLoq")) { | ||||
|             elements_button_right(canvas, "Save"); | ||||
|             elements_button_center(canvas, "Send"); | ||||
|         } | ||||
|         break; | ||||
| 
 | ||||
|     default: | ||||
| 
 | ||||
|         break; | ||||
|     } | ||||
| 
 | ||||
|     string_clear(str_buff); | ||||
| } | ||||
| 
 | ||||
| void subghz_receiver_history_full(void* context) { | ||||
|     furi_assert(context); | ||||
|     SubghzReceiver* subghz_receiver = context; | ||||
|     subghz_receiver->callback(SubghzReceverEventSendHistoryFull, subghz_receiver->context); | ||||
|     subghz_receiver->hopper_state = SubGhzHopperStateOFF; | ||||
| } | ||||
| 
 | ||||
| bool subghz_receiver_input(InputEvent* event, void* context) { | ||||
|     furi_assert(context); | ||||
| 
 | ||||
|     uint8_t scene = 0; | ||||
|     SubghzReceiver* subghz_receiver = context; | ||||
| 
 | ||||
|     if(event->type != InputTypeShort) return false; | ||||
| 
 | ||||
|     bool can_be_saved = false; | ||||
|     with_view_model( | ||||
|         subghz_receiver->view, (SubghzReceiverModel * model) { | ||||
|             can_be_saved = | ||||
|                 (model->protocol && model->protocol->to_save_string && | ||||
|                  strcmp(model->protocol->name, "KeeLoq")); | ||||
|             scene = model->scene; | ||||
|             return false; | ||||
|         }); | ||||
| 
 | ||||
|     bool can_be_saved = false; | ||||
| 
 | ||||
|     switch(scene) { | ||||
|     case ReceiverSceneMain: | ||||
|         if(event->key == InputKeyBack && event->type == InputTypeShort) { | ||||
|             with_view_model( | ||||
|                 subghz_receiver->view, (SubghzReceiverModel * model) { | ||||
|                     model->idx = 0; | ||||
|                     model->list_offset = 0; | ||||
|                     model->history_item = 0; | ||||
|                     subghz_history_clean(model->history); | ||||
|                     return true; | ||||
|                 }); | ||||
|             return false; | ||||
|         } else if( | ||||
|             event->key == InputKeyUp && | ||||
|             (event->type == InputTypeShort || event->type == InputTypeRepeat)) { | ||||
|             with_view_model( | ||||
|                 subghz_receiver->view, (SubghzReceiverModel * model) { | ||||
|                     if(model->idx != 0) model->idx--; | ||||
|                     return true; | ||||
|                 }); | ||||
|         } else if( | ||||
|             event->key == InputKeyDown && | ||||
|             (event->type == InputTypeShort || event->type == InputTypeRepeat)) { | ||||
|             with_view_model( | ||||
|                 subghz_receiver->view, (SubghzReceiverModel * model) { | ||||
|                     if(model->idx != subghz_history_get_item(model->history) - 1) model->idx++; | ||||
|                     return true; | ||||
|                 }); | ||||
|         } else if(event->key == InputKeyLeft && event->type == InputTypeShort) { | ||||
|             subghz_receiver->hopper_state = SubGhzHopperStatePause; | ||||
|             with_view_model( | ||||
|                 subghz_receiver->view, (SubghzReceiverModel * model) { | ||||
|                     model->scene = ReceiverSceneConfig; | ||||
|                     model->temp_frequency = model->frequency; | ||||
|                     return true; | ||||
|                 }); | ||||
|             subghz_receiver->callback(SubghzReceverEventConfig, subghz_receiver->context); | ||||
|         } else if(event->key == InputKeyOk && event->type == InputTypeShort) { | ||||
|             subghz_receiver->event_key_sequence = event->sequence; | ||||
|             with_view_model( | ||||
|                 subghz_receiver->view, (SubghzReceiverModel * model) { | ||||
|                     string_clean(model->text); | ||||
|                     model->protocol_result = subghz_protocol_get_by_name( | ||||
|                         subghz_receiver->protocol, | ||||
|                         subghz_history_get_name(model->history, model->idx)); | ||||
|                     if(model->protocol_result->to_load_protocol != NULL) { | ||||
|                         model->protocol_result->to_load_protocol( | ||||
|                             model->protocol_result, | ||||
|                             subghz_history_get_raw_data(model->history, model->idx)); | ||||
|                         model->protocol_result->to_string(model->protocol_result, model->text); | ||||
|                         model->scene = ReceiverSceneInfo; | ||||
|                     } | ||||
|                     return true; | ||||
|                 }); | ||||
|         } | ||||
|         break; | ||||
| 
 | ||||
|     case ReceiverSceneInfo: | ||||
|         with_view_model( | ||||
|             subghz_receiver->view, (SubghzReceiverModel * model) { | ||||
|                 can_be_saved = | ||||
|                     (model->protocol_result && model->protocol_result->to_save_string && | ||||
|                      strcmp(model->protocol_result->name, "KeeLoq")); | ||||
|                 return false; | ||||
|             }); | ||||
|         if(event->key == InputKeyBack && event->type == InputTypeShort) { | ||||
|             with_view_model( | ||||
|                 subghz_receiver->view, (SubghzReceiverModel * model) { | ||||
|                     subghz_rx_end(subghz_receiver->worker); | ||||
|                     model->real_frequency = | ||||
|                         subghz_rx(subghz_receiver->worker, subghz_frequencies[model->frequency]); | ||||
|                     subghz_receiver->hopper_state = SubGhzHopperStateRunnig; | ||||
|                     model->scene = ReceiverSceneMain; | ||||
|                     return true; | ||||
|                 }); | ||||
|             subghz_receiver->callback(SubghzReceverEventMain, subghz_receiver->context); | ||||
|         } else if(can_be_saved && event->key == InputKeyRight) { | ||||
|             subghz_receiver->callback(SubghzReceverEventSave, subghz_receiver->context); | ||||
|             return false; | ||||
|         } else if( | ||||
|             can_be_saved && event->key == InputKeyOk && event->type == InputTypePress && | ||||
|             subghz_receiver->event_key_sequence != event->sequence) { | ||||
|             subghz_receiver->hopper_state = SubGhzHopperStatePause; | ||||
|             subghz_rx_end(subghz_receiver->worker); | ||||
|             subghz_receiver->callback(SubghzReceverEventSendStart, subghz_receiver->context); | ||||
|             return true; | ||||
|         } else if( | ||||
|             can_be_saved && event->key == InputKeyOk && event->type == InputTypeRelease && | ||||
|             subghz_receiver->event_key_sequence != event->sequence) { | ||||
|             subghz_receiver->callback(SubghzReceverEventSendStop, subghz_receiver->context); | ||||
|             return true; | ||||
|         } | ||||
|         break; | ||||
| 
 | ||||
|     case ReceiverSceneConfig: | ||||
|         if(event->type != InputTypeShort) return false; | ||||
|         if(event->key == InputKeyBack) { | ||||
|             with_view_model( | ||||
|                 subghz_receiver->view, (SubghzReceiverModel * model) { | ||||
|                     model->frequency = model->temp_frequency; | ||||
|                     model->real_frequency = subghz_frequencies[model->frequency]; | ||||
|                     subghz_receiver->hopper_state = SubGhzHopperStateRunnig; | ||||
|                     if(subghz_history_get_item(model->history) == 0) { | ||||
|                         model->scene = ReceiverSceneStart; | ||||
|                     } else { | ||||
|                         model->scene = ReceiverSceneMain; | ||||
|                     } | ||||
|                     return true; | ||||
|                 }); | ||||
|             subghz_receiver->callback(SubghzReceverEventMain, subghz_receiver->context); | ||||
|         } else if(event->key == InputKeyOk) { | ||||
|             with_view_model( | ||||
|                 subghz_receiver->view, (SubghzReceiverModel * model) { | ||||
|                     if(model->frequency < subghz_frequencies_count) { | ||||
|                         subghz_rx_end(subghz_receiver->worker); | ||||
|                         model->real_frequency = subghz_rx( | ||||
|                             subghz_receiver->worker, subghz_frequencies[model->frequency]); | ||||
|                         subghz_receiver->hopper_state = SubGhzHopperStateOFF; | ||||
|                     } else { | ||||
|                         osTimerStart(subghz_receiver->timer, 1024 / 10); | ||||
|                         subghz_receiver->hopper_state = SubGhzHopperStateRunnig; | ||||
|                     } | ||||
|                     if(subghz_history_get_item(model->history) == 0) { | ||||
|                         model->scene = ReceiverSceneStart; | ||||
|                     } else { | ||||
|                         model->scene = ReceiverSceneMain; | ||||
|                     } | ||||
|                     return true; | ||||
|                 }); | ||||
|             subghz_receiver->callback(SubghzReceverEventMain, subghz_receiver->context); | ||||
|         } else { | ||||
|             with_view_model( | ||||
|                 subghz_receiver->view, (SubghzReceiverModel * model) { | ||||
|                     bool model_updated = false; | ||||
| 
 | ||||
|                     if(event->key == InputKeyLeft) { | ||||
|                         if(model->frequency > 0) model->frequency--; | ||||
|                         model_updated = true; | ||||
|                     } else if(event->key == InputKeyRight) { | ||||
|                         if(model->frequency < subghz_frequencies_count) model->frequency++; | ||||
|                         model_updated = true; | ||||
|                     } | ||||
|                     if(model_updated) { | ||||
|                         model->real_frequency = subghz_frequencies[model->frequency]; | ||||
|                     } | ||||
|                     return model_updated; | ||||
|                 }); | ||||
|         } | ||||
|         break; | ||||
| 
 | ||||
|     case ReceiverSceneStart: | ||||
|         if(event->type != InputTypeShort) return false; | ||||
|         if(event->key == InputKeyBack) { | ||||
|             return false; | ||||
|         } else if(event->key == InputKeyLeft) { | ||||
|         subghz_receiver->callback(SubghzReceverEventBack, subghz_receiver->context); | ||||
|     } else if(can_be_saved && event->key == InputKeyRight) { | ||||
|         subghz_receiver->callback(SubghzReceverEventSave, subghz_receiver->context); | ||||
|             subghz_receiver->hopper_state = SubGhzHopperStatePause; | ||||
|             with_view_model( | ||||
|                 subghz_receiver->view, (SubghzReceiverModel * model) { | ||||
|                     model->temp_frequency = model->frequency; | ||||
|                     model->scene = ReceiverSceneConfig; | ||||
|                     return true; | ||||
|                 }); | ||||
|             subghz_receiver->callback(SubghzReceverEventConfig, subghz_receiver->context); | ||||
|         } | ||||
|         break; | ||||
| 
 | ||||
|     default: | ||||
|         break; | ||||
|     } | ||||
| 
 | ||||
|     subghz_receiver_update_offset(subghz_receiver); | ||||
|     if(scene != ReceiverSceneInfo) { | ||||
|         with_view_model( | ||||
|             subghz_receiver->view, (SubghzReceiverModel * model) { | ||||
|                 if(subghz_history_get_text_space_left(model->history, NULL)) { | ||||
|                     subghz_receiver_history_full(subghz_receiver); | ||||
|                 } | ||||
|                 return false; | ||||
|             }); | ||||
|     } | ||||
| 
 | ||||
|     return true; | ||||
| @ -86,7 +480,88 @@ void subghz_receiver_text_callback(string_t text, void* context) { | ||||
|     with_view_model( | ||||
|         subghz_receiver->view, (SubghzReceiverModel * model) { | ||||
|             string_set(model->text, text); | ||||
|             model->scene = 0; | ||||
|             model->scene = ReceiverSceneMain; | ||||
|             return true; | ||||
|         }); | ||||
| } | ||||
| 
 | ||||
| void subghz_receiver_protocol_callback(SubGhzProtocolCommon* parser, void* context) { | ||||
|     furi_assert(context); | ||||
|     SubghzReceiver* subghz_receiver = context; | ||||
| 
 | ||||
|     with_view_model( | ||||
|         subghz_receiver->view, (SubghzReceiverModel * model) { | ||||
|             model->protocol_result = parser; | ||||
|             subghz_history_set_frequency_preset( | ||||
|                 model->history, | ||||
|                 model->history_item, | ||||
|                 model->real_frequency, | ||||
|                 FuriHalSubGhzPresetOok650Async); | ||||
|             subghz_history_add_to_history(model->history, parser); | ||||
| 
 | ||||
|             model->history_item = subghz_history_get_item(model->history); | ||||
|             model->scene = ReceiverSceneMain; | ||||
|             if(subghz_history_get_text_space_left(model->history, NULL)) { | ||||
|                 subghz_receiver_history_full(subghz_receiver); | ||||
|             } | ||||
|             return true; | ||||
|         }); | ||||
|     subghz_protocol_reset(subghz_receiver->protocol); | ||||
|     subghz_receiver_update_offset(subghz_receiver); | ||||
| } | ||||
| 
 | ||||
| static void subghz_receiver_timer_callback(void* context) { | ||||
|     furi_assert(context); | ||||
|     SubghzReceiver* subghz_receiver = context; | ||||
| 
 | ||||
|     switch(subghz_receiver->hopper_state) { | ||||
|     case SubGhzHopperStatePause: | ||||
|         return; | ||||
|         break; | ||||
|     case SubGhzHopperStateOFF: | ||||
|         osTimerStop(subghz_receiver->timer); | ||||
|         return; | ||||
|         break; | ||||
|     case SubGhzHopperStateRSSITimeOut: | ||||
|         if(subghz_receiver->hopper_timeout != 0) { | ||||
|             subghz_receiver->hopper_timeout--; | ||||
|             return; | ||||
|         } | ||||
|         break; | ||||
|     default: | ||||
|         break; | ||||
|     } | ||||
|     float rssi = -127.0f; | ||||
|     with_view_model( | ||||
|         subghz_receiver->view, (SubghzReceiverModel * model) { | ||||
|             if(subghz_receiver->hopper_state != SubGhzHopperStateRSSITimeOut) { | ||||
|                 // See RSSI Calculation timings in CC1101 17.3 RSSI
 | ||||
|                 rssi = furi_hal_subghz_get_rssi(); | ||||
| 
 | ||||
|                 // Stay if RSSI is high enough
 | ||||
|                 if(rssi > -90.0f) { | ||||
|                     subghz_receiver->hopper_timeout = 10; | ||||
|                     subghz_receiver->hopper_state = SubGhzHopperStateRSSITimeOut; | ||||
|                     return false; | ||||
|                 } | ||||
|             } else { | ||||
|                 subghz_receiver->hopper_state = SubGhzHopperStateRunnig; | ||||
|             } | ||||
| 
 | ||||
|             // Select next frequency
 | ||||
|             if(model->frequency < COUNT_FREQUNCY_HOPPER - 1) { | ||||
|                 model->frequency++; | ||||
|             } else { | ||||
|                 model->frequency = 0; | ||||
|             } | ||||
| 
 | ||||
|             // Restart radio
 | ||||
|             furi_hal_subghz_idle(); | ||||
|             subghz_protocol_reset(subghz_receiver->protocol); | ||||
|             model->real_frequency = furi_hal_subghz_set_frequency_and_path( | ||||
|                 subghz_frequencies_hopper[model->frequency]); | ||||
|             furi_hal_subghz_rx(); | ||||
| 
 | ||||
|             return true; | ||||
|         }); | ||||
| } | ||||
| @ -94,21 +569,37 @@ void subghz_receiver_text_callback(string_t text, void* context) { | ||||
| void subghz_receiver_enter(void* context) { | ||||
|     furi_assert(context); | ||||
|     SubghzReceiver* subghz_receiver = context; | ||||
|     //Start CC1101 Rx
 | ||||
|     subghz_begin(FuriHalSubGhzPresetOok650Async); | ||||
|     with_view_model( | ||||
|         subghz_receiver->view, (SubghzReceiverModel * model) { | ||||
|             model->protocol->to_string(model->protocol, model->text); | ||||
|             subghz_rx_end(subghz_receiver->worker); | ||||
|             model->frequency = subghz_frequencies_433_92; | ||||
|             model->real_frequency = | ||||
|                 subghz_rx(subghz_receiver->worker, subghz_frequencies[model->frequency]); | ||||
|             if(subghz_history_get_item(model->history) == 0) { | ||||
|                 model->scene = ReceiverSceneStart; | ||||
|             } else { | ||||
|                 model->scene = ReceiverSceneMain; | ||||
|             } | ||||
|             return true; | ||||
|         }); | ||||
|     subghz_protocol_enable_dump( | ||||
|         subghz_receiver->protocol, subghz_receiver_protocol_callback, subghz_receiver); | ||||
| } | ||||
| 
 | ||||
| void subghz_receiver_exit(void* context) { | ||||
|     furi_assert(context); | ||||
|     SubghzReceiver* subghz_receiver = context; | ||||
|     osTimerStop(subghz_receiver->timer); | ||||
|     with_view_model( | ||||
|         subghz_receiver->view, (SubghzReceiverModel * model) { | ||||
|             string_clean(model->text); | ||||
|             return true; | ||||
|         }); | ||||
|     // Stop CC1101 Rx
 | ||||
|     subghz_rx_end(subghz_receiver->worker); | ||||
|     subghz_sleep(); | ||||
| } | ||||
| 
 | ||||
| SubghzReceiver* subghz_receiver_alloc() { | ||||
| @ -126,8 +617,13 @@ SubghzReceiver* subghz_receiver_alloc() { | ||||
|     with_view_model( | ||||
|         subghz_receiver->view, (SubghzReceiverModel * model) { | ||||
|             string_init(model->text); | ||||
|             model->history = subghz_history_alloc(); | ||||
|             return true; | ||||
|         }); | ||||
| 
 | ||||
|     subghz_receiver->timer = | ||||
|         osTimerNew(subghz_receiver_timer_callback, osTimerPeriodic, subghz_receiver, NULL); | ||||
|     subghz_receiver->hopper_state = SubGhzHopperStateOFF; | ||||
|     return subghz_receiver; | ||||
| } | ||||
| 
 | ||||
| @ -137,8 +633,10 @@ void subghz_receiver_free(SubghzReceiver* subghz_receiver) { | ||||
|     with_view_model( | ||||
|         subghz_receiver->view, (SubghzReceiverModel * model) { | ||||
|             string_clear(model->text); | ||||
|             return true; | ||||
|             subghz_history_free(model->history); | ||||
|             return false; | ||||
|         }); | ||||
|     osTimerDelete(subghz_receiver->timer); | ||||
|     view_free(subghz_receiver->view); | ||||
|     free(subghz_receiver); | ||||
| } | ||||
| @ -147,3 +645,44 @@ View* subghz_receiver_get_view(SubghzReceiver* subghz_receiver) { | ||||
|     furi_assert(subghz_receiver); | ||||
|     return subghz_receiver->view; | ||||
| } | ||||
| 
 | ||||
| uint32_t subghz_receiver_get_frequency(SubghzReceiver* subghz_receiver) { | ||||
|     furi_assert(subghz_receiver); | ||||
|     uint32_t frequency; | ||||
|     with_view_model( | ||||
|         subghz_receiver->view, (SubghzReceiverModel * model) { | ||||
|             frequency = subghz_history_get_frequency(model->history, model->idx); | ||||
|             return false; | ||||
|         }); | ||||
|     return frequency; | ||||
| } | ||||
| 
 | ||||
| FuriHalSubGhzPreset subghz_receiver_get_preset(SubghzReceiver* subghz_receiver) { | ||||
|     furi_assert(subghz_receiver); | ||||
|     FuriHalSubGhzPreset preset; | ||||
|     with_view_model( | ||||
|         subghz_receiver->view, (SubghzReceiverModel * model) { | ||||
|             preset = subghz_history_get_preset(model->history, model->idx); | ||||
|             return false; | ||||
|         }); | ||||
|     return preset; | ||||
| } | ||||
| 
 | ||||
| void subghz_receiver_frequency_preset_to_str(SubghzReceiver* subghz_receiver, string_t output) { | ||||
|     furi_assert(subghz_receiver); | ||||
|     uint32_t frequency; | ||||
|     uint32_t preset; | ||||
|     with_view_model( | ||||
|         subghz_receiver->view, (SubghzReceiverModel * model) { | ||||
|             frequency = subghz_history_get_frequency(model->history, model->idx); | ||||
|             preset = (uint32_t)subghz_history_get_preset(model->history, model->idx); | ||||
|             return false; | ||||
|         }); | ||||
| 
 | ||||
|     string_cat_printf( | ||||
|         output, | ||||
|         "Frequency: %d\n" | ||||
|         "Preset: %d\n", | ||||
|         (int)frequency, | ||||
|         (int)preset); | ||||
| } | ||||
|  | ||||
| @ -2,10 +2,20 @@ | ||||
| 
 | ||||
| #include <gui/view.h> | ||||
| #include <lib/subghz/protocols/subghz_protocol_common.h> | ||||
| #include <lib/subghz/protocols/subghz_protocol.h> | ||||
| #include <lib/subghz/subghz_worker.h> | ||||
| #include "../subghz_history.h" | ||||
| 
 | ||||
| typedef enum { | ||||
|     SubghzReceverEventOK, | ||||
|     SubghzReceverEventConfig, | ||||
|     SubghzReceverEventMain, | ||||
|     SubghzReceverEventSave, | ||||
|     SubghzReceverEventBack, | ||||
|     SubghzReceverEventMore, | ||||
|     SubghzReceverEventSendStart, | ||||
|     SubghzReceverEventSendStop, | ||||
|     SubghzReceverEventSendHistoryFull, | ||||
| } SubghzReceverEvent; | ||||
| 
 | ||||
| typedef struct SubghzReceiver SubghzReceiver; | ||||
| @ -23,4 +33,17 @@ void subghz_receiver_free(SubghzReceiver* subghz_receiver); | ||||
| 
 | ||||
| View* subghz_receiver_get_view(SubghzReceiver* subghz_receiver); | ||||
| 
 | ||||
| void subghz_receiver_set_protocol(SubghzReceiver* subghz_receiver, SubGhzProtocolCommon* protocol); | ||||
| void subghz_receiver_set_protocol( | ||||
|     SubghzReceiver* subghz_receiver, | ||||
|     SubGhzProtocolCommon* protocol_result, | ||||
|     SubGhzProtocol* protocol); | ||||
| 
 | ||||
| SubGhzProtocolCommon* subghz_receiver_get_protocol(SubghzReceiver* subghz_receiver); | ||||
| 
 | ||||
| void subghz_receiver_set_worker(SubghzReceiver* subghz_receiver, SubGhzWorker* worker); | ||||
| 
 | ||||
| uint32_t subghz_receiver_get_frequency(SubghzReceiver* subghz_receiver); | ||||
| 
 | ||||
| FuriHalSubGhzPreset subghz_receiver_get_preset(SubghzReceiver* subghz_receiver); | ||||
| 
 | ||||
| void subghz_receiver_frequency_preset_to_str(SubghzReceiver* subghz_receiver, string_t output); | ||||
|  | ||||
| @ -1,11 +0,0 @@ | ||||
| #pragma once | ||||
| 
 | ||||
| #include <gui/view.h> | ||||
| 
 | ||||
| typedef struct SubghzStatic SubghzStatic; | ||||
| 
 | ||||
| SubghzStatic* subghz_static_alloc(); | ||||
| 
 | ||||
| void subghz_static_free(SubghzStatic* subghz_static); | ||||
| 
 | ||||
| View* subghz_static_get_view(SubghzStatic* subghz_static); | ||||
| @ -71,16 +71,14 @@ bool subghz_test_carrier_input(InputEvent* event, void* context) { | ||||
|     furi_assert(context); | ||||
|     SubghzTestCarrier* subghz_test_carrier = context; | ||||
| 
 | ||||
|     if(event->key == InputKeyBack) { | ||||
|     if(event->key == InputKeyBack || event->type != InputTypeShort) { | ||||
|         return false; | ||||
|     } | ||||
| 
 | ||||
|     with_view_model( | ||||
|         subghz_test_carrier->view, (SubghzTestCarrierModel * model) { | ||||
|             osTimerStop(subghz_test_carrier->timer); | ||||
|             furi_hal_subghz_idle(); | ||||
| 
 | ||||
|             if(event->type == InputTypeShort) { | ||||
|             if(event->key == InputKeyLeft) { | ||||
|                 if(model->frequency > 0) model->frequency--; | ||||
|             } else if(event->key == InputKeyRight) { | ||||
| @ -100,12 +98,10 @@ bool subghz_test_carrier_input(InputEvent* event, void* context) { | ||||
|             model->real_frequency = | ||||
|                 furi_hal_subghz_set_frequency(subghz_frequencies[model->frequency]); | ||||
|             furi_hal_subghz_set_path(model->path); | ||||
|             } | ||||
| 
 | ||||
|             if(model->status == SubghzTestCarrierModelStatusRx) { | ||||
|                 hal_gpio_init(&gpio_cc1101_g0, GpioModeInput, GpioPullNo, GpioSpeedLow); | ||||
|                 furi_hal_subghz_rx(); | ||||
|                 osTimerStart(subghz_test_carrier->timer, 1024 / 4); | ||||
|             } else { | ||||
|                 hal_gpio_init(&gpio_cc1101_g0, GpioModeOutputPushPull, GpioPullNo, GpioSpeedLow); | ||||
|                 hal_gpio_write(&gpio_cc1101_g0, true); | ||||
| @ -123,7 +119,7 @@ void subghz_test_carrier_enter(void* context) { | ||||
|     SubghzTestCarrier* subghz_test_carrier = context; | ||||
| 
 | ||||
|     furi_hal_subghz_reset(); | ||||
|     furi_hal_subghz_load_preset(FuriHalSubGhzPresetOokAsync); | ||||
|     furi_hal_subghz_load_preset(FuriHalSubGhzPresetOok650Async); | ||||
| 
 | ||||
|     hal_gpio_init(&gpio_cc1101_g0, GpioModeInput, GpioPullNo, GpioSpeedLow); | ||||
| 
 | ||||
| @ -159,8 +155,11 @@ void subghz_test_carrier_rssi_timer_callback(void* context) { | ||||
| 
 | ||||
|     with_view_model( | ||||
|         subghz_test_carrier->view, (SubghzTestCarrierModel * model) { | ||||
|             if(model->status == SubghzTestCarrierModelStatusRx) { | ||||
|                 model->rssi = furi_hal_subghz_get_rssi(); | ||||
|                 return true; | ||||
|             } | ||||
|             return false; | ||||
|         }); | ||||
| } | ||||
| 
 | ||||
| @ -170,7 +169,7 @@ SubghzTestCarrier* subghz_test_carrier_alloc() { | ||||
|     // View allocation and configuration
 | ||||
|     subghz_test_carrier->view = view_alloc(); | ||||
|     view_allocate_model( | ||||
|         subghz_test_carrier->view, ViewModelTypeLockFree, sizeof(SubghzTestCarrierModel)); | ||||
|         subghz_test_carrier->view, ViewModelTypeLocking, sizeof(SubghzTestCarrierModel)); | ||||
|     view_set_context(subghz_test_carrier->view, subghz_test_carrier); | ||||
|     view_set_draw_callback(subghz_test_carrier->view, (ViewDrawCallback)subghz_test_carrier_draw); | ||||
|     view_set_input_callback(subghz_test_carrier->view, subghz_test_carrier_input); | ||||
|  | ||||
| @ -116,7 +116,7 @@ static bool subghz_test_packet_input(InputEvent* event, void* context) { | ||||
|     furi_assert(context); | ||||
|     SubghzTestPacket* instance = context; | ||||
| 
 | ||||
|     if(event->key == InputKeyBack) { | ||||
|     if(event->key == InputKeyBack || event->type != InputTypeShort) { | ||||
|         return false; | ||||
|     } | ||||
| 
 | ||||
| @ -128,7 +128,6 @@ static bool subghz_test_packet_input(InputEvent* event, void* context) { | ||||
|                 furi_hal_subghz_stop_async_tx(); | ||||
|             } | ||||
| 
 | ||||
|             if(event->type == InputTypeShort) { | ||||
|             if(event->key == InputKeyLeft) { | ||||
|                 if(model->frequency > 0) model->frequency--; | ||||
|             } else if(event->key == InputKeyRight) { | ||||
| @ -148,7 +147,6 @@ static bool subghz_test_packet_input(InputEvent* event, void* context) { | ||||
|             model->real_frequency = | ||||
|                 furi_hal_subghz_set_frequency(subghz_frequencies[model->frequency]); | ||||
|             furi_hal_subghz_set_path(model->path); | ||||
|             } | ||||
| 
 | ||||
|             if(model->status == SubghzTestPacketModelStatusRx) { | ||||
|                 furi_hal_subghz_start_async_rx(subghz_test_packet_rx_callback, instance); | ||||
| @ -168,7 +166,7 @@ void subghz_test_packet_enter(void* context) { | ||||
|     SubghzTestPacket* instance = context; | ||||
| 
 | ||||
|     furi_hal_subghz_reset(); | ||||
|     furi_hal_subghz_load_preset(FuriHalSubGhzPresetOokAsync); | ||||
|     furi_hal_subghz_load_preset(FuriHalSubGhzPresetOok650Async); | ||||
| 
 | ||||
|     with_view_model( | ||||
|         instance->view, (SubghzTestPacketModel * model) { | ||||
| @ -210,7 +208,7 @@ SubghzTestPacket* subghz_test_packet_alloc() { | ||||
| 
 | ||||
|     // View allocation and configuration
 | ||||
|     instance->view = view_alloc(); | ||||
|     view_allocate_model(instance->view, ViewModelTypeLockFree, sizeof(SubghzTestPacketModel)); | ||||
|     view_allocate_model(instance->view, ViewModelTypeLocking, sizeof(SubghzTestPacketModel)); | ||||
|     view_set_context(instance->view, instance); | ||||
|     view_set_draw_callback(instance->view, (ViewDrawCallback)subghz_test_packet_draw); | ||||
|     view_set_input_callback(instance->view, subghz_test_packet_input); | ||||
|  | ||||
| @ -1,4 +1,4 @@ | ||||
| #include "subghz_static.h" | ||||
| #include "subghz_test_static.h" | ||||
| #include "../subghz_i.h" | ||||
| 
 | ||||
| #include <math.h> | ||||
| @ -8,30 +8,30 @@ | ||||
| #include <notification/notification-messages.h> | ||||
| #include <lib/subghz/protocols/subghz_protocol_princeton.h> | ||||
| 
 | ||||
| static const uint32_t subghz_static_keys[] = { | ||||
| static const uint32_t subghz_test_static_keys[] = { | ||||
|     0x0074BADE, | ||||
|     0x0074BADD, | ||||
|     0x0074BADB, | ||||
|     0x00E34A4E, | ||||
| }; | ||||
| 
 | ||||
| struct SubghzStatic { | ||||
| struct SubghzTestStatic { | ||||
|     View* view; | ||||
|     SubGhzEncoderPrinceton* encoder; | ||||
| }; | ||||
| 
 | ||||
| typedef enum { | ||||
|     SubghzStaticStatusRx, | ||||
|     SubghzStaticStatusTx, | ||||
| } SubghzStaticStatus; | ||||
|     SubghzTestStaticStatusRx, | ||||
|     SubghzTestStaticStatusTx, | ||||
| } SubghzTestStaticStatus; | ||||
| 
 | ||||
| typedef struct { | ||||
|     uint8_t frequency; | ||||
|     uint32_t real_frequency; | ||||
|     uint8_t button; | ||||
| } SubghzStaticModel; | ||||
| } SubghzTestStaticModel; | ||||
| 
 | ||||
| void subghz_static_draw(Canvas* canvas, SubghzStaticModel* model) { | ||||
| void subghz_test_static_draw(Canvas* canvas, SubghzTestStaticModel* model) { | ||||
|     char buffer[64]; | ||||
| 
 | ||||
|     canvas_set_color(canvas, ColorBlack); | ||||
| @ -52,24 +52,21 @@ void subghz_static_draw(Canvas* canvas, SubghzStaticModel* model) { | ||||
|     canvas_draw_str(canvas, 0, 31, buffer); | ||||
| } | ||||
| 
 | ||||
| bool subghz_static_input(InputEvent* event, void* context) { | ||||
| bool subghz_test_static_input(InputEvent* event, void* context) { | ||||
|     furi_assert(context); | ||||
|     SubghzStatic* instance = context; | ||||
|     SubghzTestStatic* instance = context; | ||||
| 
 | ||||
|     if(event->key == InputKeyBack) { | ||||
|         return false; | ||||
|     } | ||||
| 
 | ||||
|     with_view_model( | ||||
|         instance->view, (SubghzStaticModel * model) { | ||||
|             bool reconfigure = false; | ||||
|         instance->view, (SubghzTestStaticModel * model) { | ||||
|             if(event->type == InputTypeShort) { | ||||
|                 if(event->key == InputKeyLeft) { | ||||
|                     if(model->frequency > 0) model->frequency--; | ||||
|                     reconfigure = true; | ||||
|                 } else if(event->key == InputKeyRight) { | ||||
|                     if(model->frequency < subghz_frequencies_count - 1) model->frequency++; | ||||
|                     reconfigure = true; | ||||
|                 } else if(event->key == InputKeyDown) { | ||||
|                     if(model->button > 0) model->button--; | ||||
|                 } else if(event->key == InputKeyUp) { | ||||
| @ -77,28 +74,29 @@ bool subghz_static_input(InputEvent* event, void* context) { | ||||
|                 } | ||||
|             } | ||||
| 
 | ||||
|             if(reconfigure) { | ||||
|                 furi_hal_subghz_idle(); | ||||
|                 model->real_frequency = | ||||
|                     furi_hal_subghz_set_frequency_and_path(subghz_frequencies[model->frequency]); | ||||
|                 furi_hal_subghz_tx(); | ||||
|             } | ||||
|             model->real_frequency = subghz_frequencies[model->frequency]; | ||||
| 
 | ||||
|             if(event->key == InputKeyOk) { | ||||
|                 if(event->type == InputTypePress) { | ||||
|                 NotificationApp* notification = furi_record_open("notification"); | ||||
|                 if(event->type == InputTypePress) { | ||||
|                     notification_message_block(notification, &sequence_set_red_255); | ||||
| 
 | ||||
|                     FURI_LOG_I("SubghzTestStatic", "TX Start"); | ||||
|                     furi_hal_subghz_idle(); | ||||
|                     furi_hal_subghz_set_frequency_and_path(subghz_frequencies[model->frequency]); | ||||
| 
 | ||||
|                     subghz_encoder_princeton_set( | ||||
|                         instance->encoder, subghz_static_keys[model->button], 20); | ||||
|                         instance->encoder, subghz_test_static_keys[model->button], 10000); | ||||
| 
 | ||||
|                     furi_hal_subghz_start_async_tx( | ||||
|                         subghz_encoder_princeton_yield, instance->encoder); | ||||
|                     while(!furi_hal_subghz_is_async_tx_complete()) osDelay(33); | ||||
|                 } else if(event->type == InputTypeRelease) { | ||||
|                     FURI_LOG_I("SubghzTestStatic", "TX Stop"); | ||||
|                     furi_hal_subghz_stop_async_tx(); | ||||
| 
 | ||||
|                     notification_message(notification, &sequence_reset_red); | ||||
|                     furi_record_close("notification"); | ||||
|                 } | ||||
|                 furi_record_close("notification"); | ||||
|             } | ||||
| 
 | ||||
|             return true; | ||||
| @ -107,58 +105,55 @@ bool subghz_static_input(InputEvent* event, void* context) { | ||||
|     return true; | ||||
| } | ||||
| 
 | ||||
| void subghz_static_enter(void* context) { | ||||
| void subghz_test_static_enter(void* context) { | ||||
|     furi_assert(context); | ||||
|     SubghzStatic* instance = context; | ||||
|     SubghzTestStatic* instance = context; | ||||
| 
 | ||||
|     furi_hal_subghz_reset(); | ||||
|     furi_hal_subghz_load_preset(FuriHalSubGhzPresetOokAsync); | ||||
|     furi_hal_subghz_load_preset(FuriHalSubGhzPresetOok650Async); | ||||
| 
 | ||||
|     hal_gpio_init(&gpio_cc1101_g0, GpioModeOutputPushPull, GpioPullNo, GpioSpeedLow); | ||||
|     hal_gpio_write(&gpio_cc1101_g0, false); | ||||
| 
 | ||||
|     with_view_model( | ||||
|         instance->view, (SubghzStaticModel * model) { | ||||
|         instance->view, (SubghzTestStaticModel * model) { | ||||
|             model->frequency = subghz_frequencies_433_92; | ||||
|             model->real_frequency = | ||||
|                 furi_hal_subghz_set_frequency_and_path(subghz_frequencies[model->frequency]); | ||||
|             model->real_frequency = subghz_frequencies[model->frequency]; | ||||
|             model->button = 0; | ||||
|             return true; | ||||
|         }); | ||||
| 
 | ||||
|     furi_hal_subghz_tx(); | ||||
| } | ||||
| 
 | ||||
| void subghz_static_exit(void* context) { | ||||
| void subghz_test_static_exit(void* context) { | ||||
|     furi_assert(context); | ||||
|     furi_hal_subghz_sleep(); | ||||
| } | ||||
| 
 | ||||
| SubghzStatic* subghz_static_alloc() { | ||||
|     SubghzStatic* instance = furi_alloc(sizeof(SubghzStatic)); | ||||
| SubghzTestStatic* subghz_test_static_alloc() { | ||||
|     SubghzTestStatic* instance = furi_alloc(sizeof(SubghzTestStatic)); | ||||
| 
 | ||||
|     // View allocation and configuration
 | ||||
|     instance->view = view_alloc(); | ||||
|     view_allocate_model(instance->view, ViewModelTypeLockFree, sizeof(SubghzStaticModel)); | ||||
|     view_allocate_model(instance->view, ViewModelTypeLocking, sizeof(SubghzTestStaticModel)); | ||||
|     view_set_context(instance->view, instance); | ||||
|     view_set_draw_callback(instance->view, (ViewDrawCallback)subghz_static_draw); | ||||
|     view_set_input_callback(instance->view, subghz_static_input); | ||||
|     view_set_enter_callback(instance->view, subghz_static_enter); | ||||
|     view_set_exit_callback(instance->view, subghz_static_exit); | ||||
|     view_set_draw_callback(instance->view, (ViewDrawCallback)subghz_test_static_draw); | ||||
|     view_set_input_callback(instance->view, subghz_test_static_input); | ||||
|     view_set_enter_callback(instance->view, subghz_test_static_enter); | ||||
|     view_set_exit_callback(instance->view, subghz_test_static_exit); | ||||
| 
 | ||||
|     instance->encoder = subghz_encoder_princeton_alloc(); | ||||
| 
 | ||||
|     return instance; | ||||
| } | ||||
| 
 | ||||
| void subghz_static_free(SubghzStatic* instance) { | ||||
| void subghz_test_static_free(SubghzTestStatic* instance) { | ||||
|     furi_assert(instance); | ||||
|     subghz_encoder_princeton_free(instance->encoder); | ||||
|     view_free(instance->view); | ||||
|     free(instance); | ||||
| } | ||||
| 
 | ||||
| View* subghz_static_get_view(SubghzStatic* instance) { | ||||
| View* subghz_test_static_get_view(SubghzTestStatic* instance) { | ||||
|     furi_assert(instance); | ||||
|     return instance->view; | ||||
| } | ||||
							
								
								
									
										11
									
								
								applications/subghz/views/subghz_test_static.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @ -0,0 +1,11 @@ | ||||
| #pragma once | ||||
| 
 | ||||
| #include <gui/view.h> | ||||
| 
 | ||||
| typedef struct SubghzTestStatic SubghzTestStatic; | ||||
| 
 | ||||
| SubghzTestStatic* subghz_test_static_alloc(); | ||||
| 
 | ||||
| void subghz_test_static_free(SubghzTestStatic* subghz_static); | ||||
| 
 | ||||
| View* subghz_test_static_get_view(SubghzTestStatic* subghz_static); | ||||
| @ -7,8 +7,7 @@ | ||||
| #include <input/input.h> | ||||
| #include <gui/elements.h> | ||||
| #include <notification/notification-messages.h> | ||||
| 
 | ||||
| #include <assets_icons.h> | ||||
| #include <lib/subghz/protocols/subghz_protocol_keeloq.h> | ||||
| 
 | ||||
| struct SubghzTransmitter { | ||||
|     View* view; | ||||
| @ -19,6 +18,8 @@ struct SubghzTransmitter { | ||||
| typedef struct { | ||||
|     string_t text; | ||||
|     uint16_t scene; | ||||
|     uint32_t real_frequency; | ||||
|     FuriHalSubGhzPreset preset; | ||||
|     SubGhzProtocolCommon* protocol; | ||||
| } SubghzTransmitterModel; | ||||
| 
 | ||||
| @ -42,36 +43,102 @@ void subghz_transmitter_set_protocol( | ||||
|         }); | ||||
| } | ||||
| 
 | ||||
| void subghz_transmitter_set_frequency_preset( | ||||
|     SubghzTransmitter* subghz_transmitter, | ||||
|     uint32_t frequency, | ||||
|     FuriHalSubGhzPreset preset) { | ||||
|     with_view_model( | ||||
|         subghz_transmitter->view, (SubghzTransmitterModel * model) { | ||||
|             model->real_frequency = frequency; | ||||
|             model->preset = preset; | ||||
|             return true; | ||||
|         }); | ||||
| } | ||||
| 
 | ||||
| static void subghz_transmitter_button_right(Canvas* canvas, const char* str) { | ||||
|     const uint8_t button_height = 13; | ||||
|     const uint8_t vertical_offset = 3; | ||||
|     const uint8_t horizontal_offset = 1; | ||||
|     const uint8_t string_width = canvas_string_width(canvas, str); | ||||
|     const Icon* icon = &I_ButtonCenter_7x7; | ||||
|     const uint8_t icon_offset = 3; | ||||
|     const uint8_t icon_width_with_offset = icon_get_width(icon) + icon_offset; | ||||
|     const uint8_t button_width = string_width + horizontal_offset * 2 + icon_width_with_offset; | ||||
| 
 | ||||
|     const uint8_t x = (canvas_width(canvas) - button_width) / 2 + 40; | ||||
|     const uint8_t y = canvas_height(canvas); | ||||
| 
 | ||||
|     canvas_draw_box(canvas, x, y - button_height, button_width, button_height); | ||||
| 
 | ||||
|     canvas_draw_line(canvas, x - 1, y, x - 1, y - button_height + 0); | ||||
|     canvas_draw_line(canvas, x - 2, y, x - 2, y - button_height + 1); | ||||
|     canvas_draw_line(canvas, x - 3, y, x - 3, y - button_height + 2); | ||||
| 
 | ||||
|     canvas_draw_line(canvas, x + button_width + 0, y, x + button_width + 0, y - button_height + 0); | ||||
|     canvas_draw_line(canvas, x + button_width + 1, y, x + button_width + 1, y - button_height + 1); | ||||
|     canvas_draw_line(canvas, x + button_width + 2, y, x + button_width + 2, y - button_height + 2); | ||||
| 
 | ||||
|     canvas_invert_color(canvas); | ||||
|     canvas_draw_icon( | ||||
|         canvas, x + horizontal_offset, y - button_height + vertical_offset, &I_ButtonCenter_7x7); | ||||
|     canvas_draw_str( | ||||
|         canvas, x + horizontal_offset + icon_width_with_offset, y - vertical_offset, str); | ||||
|     canvas_invert_color(canvas); | ||||
| } | ||||
| 
 | ||||
| void subghz_transmitter_draw(Canvas* canvas, SubghzTransmitterModel* model) { | ||||
|     char buffer[64]; | ||||
|     canvas_clear(canvas); | ||||
|     canvas_set_color(canvas, ColorBlack); | ||||
|     canvas_set_font(canvas, FontSecondary); | ||||
|     elements_multiline_text(canvas, 0, 10, string_get_cstr(model->text)); | ||||
|     elements_multiline_text(canvas, 0, 8, string_get_cstr(model->text)); | ||||
|     snprintf( | ||||
|         buffer, | ||||
|         sizeof(buffer), | ||||
|         "%03ld.%03ld", | ||||
|         model->real_frequency / 1000000 % 1000, | ||||
|         model->real_frequency / 1000 % 1000); | ||||
|     canvas_draw_str(canvas, 90, 8, buffer); | ||||
| 
 | ||||
|     if(model->protocol && model->protocol->get_upload_protocol) { | ||||
|         elements_button_center(canvas, "Send"); | ||||
|         if((!strcmp(model->protocol->name, "KeeLoq")) && | ||||
|            (!strcmp(subghz_protocol_keeloq_get_manufacture_name(model->protocol), "Unknown"))) { | ||||
|             return; | ||||
|         } | ||||
|         subghz_transmitter_button_right(canvas, "Send"); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| bool subghz_transmitter_input(InputEvent* event, void* context) { | ||||
|     furi_assert(context); | ||||
|     SubghzTransmitter* subghz_transmitter = context; | ||||
|     bool can_be_send = false; | ||||
|     bool can_be_sent = false; | ||||
| 
 | ||||
|     if(event->key == InputKeyBack) { | ||||
|         return false; | ||||
|     } | ||||
| 
 | ||||
|     with_view_model( | ||||
|         subghz_transmitter->view, (SubghzTransmitterModel * model) { | ||||
|             can_be_send = (model->protocol && model->protocol->get_upload_protocol); | ||||
|             if(model->protocol && model->protocol->get_upload_protocol) { | ||||
|                 if((!strcmp(model->protocol->name, "KeeLoq")) && | ||||
|                    (!strcmp( | ||||
|                        subghz_protocol_keeloq_get_manufacture_name(model->protocol), "Unknown"))) { | ||||
|                     return false; | ||||
|                 } | ||||
|                 can_be_sent = true; | ||||
|             } | ||||
|             //can_be_sent = (model->protocol && model->protocol->get_upload_protocol);
 | ||||
|             string_clean(model->text); | ||||
|             model->protocol->to_string(model->protocol, model->text); | ||||
|             return true; | ||||
|         }); | ||||
|     //if(event->type != InputTypeShort) return false;
 | ||||
| 
 | ||||
|     if(event->key == InputKeyBack) { | ||||
|         return false; | ||||
|     } else if(can_be_send && event->key == InputKeyOk && event->type == InputTypePress) { | ||||
|     if(can_be_sent && event->key == InputKeyOk && event->type == InputTypePress) { | ||||
|         subghz_transmitter->callback(SubghzTransmitterEventSendStart, subghz_transmitter->context); | ||||
|         return true; | ||||
|     } else if(can_be_send && event->key == InputKeyOk && event->type == InputTypeRelease) { | ||||
|     } else if(can_be_sent && event->key == InputKeyOk && event->type == InputTypeRelease) { | ||||
|         subghz_transmitter->callback(SubghzTransmitterEventSendStop, subghz_transmitter->context); | ||||
|         return true; | ||||
|     } | ||||
| @ -96,6 +163,7 @@ void subghz_transmitter_enter(void* context) { | ||||
|     SubghzTransmitter* subghz_transmitter = context; | ||||
|     with_view_model( | ||||
|         subghz_transmitter->view, (SubghzTransmitterModel * model) { | ||||
|             string_clean(model->text); | ||||
|             model->protocol->to_string(model->protocol, model->text); | ||||
|             return true; | ||||
|         }); | ||||
|  | ||||
| @ -7,6 +7,7 @@ typedef enum { | ||||
|     SubghzTransmitterEventSendStart, | ||||
|     SubghzTransmitterEventSendStop, | ||||
|     SubghzTransmitterEventBack, | ||||
|     SubghzTransmitterEventNoMan, | ||||
| } SubghzTransmitterEvent; | ||||
| 
 | ||||
| typedef struct SubghzTransmitter SubghzTransmitter; | ||||
| @ -27,3 +28,7 @@ View* subghz_transmitter_get_view(SubghzTransmitter* subghz_transmitter); | ||||
| void subghz_transmitter_set_protocol( | ||||
|     SubghzTransmitter* subghz_transmitter, | ||||
|     SubGhzProtocolCommon* protocol); | ||||
| void subghz_transmitter_set_frequency_preset( | ||||
|     SubghzTransmitter* subghz_transmitter, | ||||
|     uint32_t frequency, | ||||
|     FuriHalSubGhzPreset preset); | ||||
|  | ||||
| @ -1,11 +1,12 @@ | ||||
| #include <furi.h> | ||||
| #include "../minunit.h" | ||||
| #include "irda.h" | ||||
| #include "irda_common_i.h" | ||||
| #include "common/irda_common_i.h" | ||||
| #include "test_data/irda_nec_test_data.srcdata" | ||||
| #include "test_data/irda_necext_test_data.srcdata" | ||||
| #include "test_data/irda_samsung_test_data.srcdata" | ||||
| #include "test_data/irda_rc6_test_data.srcdata" | ||||
| #include "test_data/irda_rc5_test_data.srcdata" | ||||
| 
 | ||||
| #define RUN_ENCODER(data, expected) \ | ||||
|     run_encoder((data), COUNT_OF(data), (expected), COUNT_OF(expected)) | ||||
| @ -153,16 +154,19 @@ MU_TEST(test_decoder_samsung32) { | ||||
| } | ||||
| 
 | ||||
| MU_TEST(test_mix) { | ||||
|     RUN_DECODER(test_decoder_rc5_input2, test_decoder_rc5_expected2); | ||||
|     RUN_DECODER(test_decoder_necext_input1, test_decoder_necext_expected1); | ||||
|     // can use encoder data for decoding, but can't do opposite
 | ||||
|     RUN_DECODER(test_encoder_rc6_expected1, test_encoder_rc6_input1); | ||||
|     RUN_DECODER(test_decoder_samsung32_input1, test_decoder_samsung32_expected1); | ||||
|     RUN_DECODER(test_decoder_rc6_input1, test_decoder_rc6_expected1); | ||||
|     RUN_DECODER(test_decoder_samsung32_input1, test_decoder_samsung32_expected1); | ||||
|     RUN_DECODER(test_decoder_rc5_input1, test_decoder_rc5_expected1); | ||||
|     RUN_DECODER(test_decoder_necext_input1, test_decoder_necext_expected1); | ||||
|     RUN_DECODER(test_decoder_nec_input2, test_decoder_nec_expected2); | ||||
|     RUN_DECODER(test_decoder_rc6_input1, test_decoder_rc6_expected1); | ||||
|     RUN_DECODER(test_decoder_necext_input1, test_decoder_necext_expected1); | ||||
|     RUN_DECODER(test_decoder_rc5_input5, test_decoder_rc5_expected5); | ||||
|     RUN_DECODER(test_decoder_samsung32_input1, test_decoder_samsung32_expected1); | ||||
| } | ||||
| 
 | ||||
| @ -187,6 +191,25 @@ MU_TEST(test_decoder_necext1) { | ||||
|     RUN_DECODER(test_decoder_necext_input1, test_decoder_necext_expected1); | ||||
| } | ||||
| 
 | ||||
| MU_TEST(test_decoder_rc5) { | ||||
|     RUN_DECODER(test_decoder_rc5x_input1, test_decoder_rc5x_expected1); | ||||
|     RUN_DECODER(test_decoder_rc5_input1, test_decoder_rc5_expected1); | ||||
|     RUN_DECODER(test_decoder_rc5_input2, test_decoder_rc5_expected2); | ||||
|     RUN_DECODER(test_decoder_rc5_input3, test_decoder_rc5_expected3); | ||||
|     RUN_DECODER(test_decoder_rc5_input4, test_decoder_rc5_expected4); | ||||
|     RUN_DECODER(test_decoder_rc5_input5, test_decoder_rc5_expected5); | ||||
|     RUN_DECODER(test_decoder_rc5_input6, test_decoder_rc5_expected6); | ||||
|     RUN_DECODER(test_decoder_rc5_input_all_repeats, test_decoder_rc5_expected_all_repeats); | ||||
| } | ||||
| 
 | ||||
| MU_TEST(test_encoder_rc5x) { | ||||
|     RUN_ENCODER(test_decoder_rc5x_expected1, test_decoder_rc5x_input1); | ||||
| } | ||||
| 
 | ||||
| MU_TEST(test_encoder_rc5) { | ||||
|     RUN_ENCODER(test_decoder_rc5_expected_all_repeats, test_decoder_rc5_input_all_repeats); | ||||
| } | ||||
| 
 | ||||
| MU_TEST(test_decoder_rc6) { | ||||
|     RUN_DECODER(test_decoder_rc6_input1, test_decoder_rc6_expected1); | ||||
| } | ||||
| @ -200,20 +223,24 @@ MU_TEST(test_encoder_decoder_all) { | ||||
|     run_encoder_decoder(test_necext_all, COUNT_OF(test_necext_all)); | ||||
|     run_encoder_decoder(test_samsung32_all, COUNT_OF(test_samsung32_all)); | ||||
|     run_encoder_decoder(test_rc6_all, COUNT_OF(test_rc6_all)); | ||||
|     run_encoder_decoder(test_rc5_all, COUNT_OF(test_rc5_all)); | ||||
| } | ||||
| 
 | ||||
| MU_TEST_SUITE(test_irda_decoder_encoder) { | ||||
|     MU_SUITE_CONFIGURE(&test_setup, &test_teardown); | ||||
| 
 | ||||
|     MU_RUN_TEST(test_encoder_decoder_all); | ||||
|     MU_RUN_TEST(test_encoder_rc5x); | ||||
|     MU_RUN_TEST(test_encoder_rc5); | ||||
|     MU_RUN_TEST(test_decoder_rc5); | ||||
|     MU_RUN_TEST(test_decoder_rc6); | ||||
|     MU_RUN_TEST(test_encoder_rc6); | ||||
|     MU_RUN_TEST(test_decoder_unexpected_end_in_sequence); | ||||
|     MU_RUN_TEST(test_decoder_nec1); | ||||
|     MU_RUN_TEST(test_decoder_nec2); | ||||
|     MU_RUN_TEST(test_decoder_samsung32); | ||||
|     MU_RUN_TEST(test_decoder_necext1); | ||||
|     MU_RUN_TEST(test_mix); | ||||
|     MU_RUN_TEST(test_decoder_rc6); | ||||
|     MU_RUN_TEST(test_encoder_rc6); | ||||
|     MU_RUN_TEST(test_encoder_decoder_all); | ||||
| } | ||||
| 
 | ||||
| int run_minunit_test_irda_decoder_encoder() { | ||||
|  | ||||
| @ -0,0 +1,160 @@ | ||||
| /* | ||||
| _______----__--____----__--____--__----____----__--__--__--__ | ||||
|      | 1 | 0 | 0 | 1 | 0 | 0 | 1 | 1 | 0 | 1 | 0 | 0 | 0 | 0 | | ||||
|       s1  s2   t |     address       |        command        | | ||||
| */ | ||||
| 
 | ||||
| const uint32_t test_decoder_rc5x_input1[] = { | ||||
| 27000 + 888, 1776, 888, 888, 1776, 1776, 888, 888, 1776, 888, 888, 1776, 1776, 1776, 888, 888, 888, 888, 888, 888, | ||||
| }; | ||||
| 
 | ||||
| const IrdaMessage test_decoder_rc5x_expected1[] = { | ||||
|     {IrdaProtocolRC5X,     0x13,      0x10,  false}, // toggle 0 | ||||
| }; | ||||
| 
 | ||||
| /* | ||||
| _______--__----____----__--____--__----____----__--__--__--__ | ||||
|      | 1 | 1 | 0 | 1 | 0 | 0 | 1 | 1 | 0 | 1 | 0 | 0 | 0 | 0 | | ||||
|       s1  s2   t |     address       |        command        | | ||||
| */ | ||||
| 
 | ||||
| const uint32_t test_decoder_rc5_input1[] = { | ||||
| 27000 + 888, 888, 888, 1776, 1776, 1776, 888, 888, 1776, 888, 888, 1776, 1776, 1776, 888, 888, 888, 888, 888, 888, | ||||
| }; | ||||
| 
 | ||||
| const IrdaMessage test_decoder_rc5_expected1[] = { | ||||
|     {IrdaProtocolRC5,     0x13,      0x10,  false}, // toggle 0 | ||||
| }; | ||||
| 
 | ||||
| 
 | ||||
| /* | ||||
| _______--__--__--__----__--____--__----____----__--__--__--__ | ||||
|      | 1 | 1 | 1 | 1 | 0 | 0 | 1 | 1 | 0 | 1 | 0 | 0 | 0 | 0 | | ||||
|       s1  s2   t |     address       |        command        | | ||||
| */ | ||||
| 
 | ||||
| const uint32_t test_decoder_rc5_input2[] = { | ||||
| 27000 + 888, 888, 888, 888, 888, 888, 888, 1776, 888, 888, 1776, 888, 888, 1776, 1776, 1776, 888, 888, 888, 888, 888, 888, | ||||
| }; | ||||
| 
 | ||||
| const IrdaMessage test_decoder_rc5_expected2[] = { | ||||
|     {IrdaProtocolRC5,     0x13,      0x10,  false}, // toggle 1 | ||||
| }; | ||||
| 
 | ||||
| /* | ||||
| _______--__----____----__--____--__----____----__--__--____-- | ||||
|      | 1 | 1 | 0 | 1 | 0 | 0 | 1 | 1 | 0 | 1 | 0 | 0 | 0 | 1 | | ||||
|       s1  s2   t |     address       |        command        | | ||||
| */ | ||||
| 
 | ||||
| const uint32_t test_decoder_rc5_input3[] = { | ||||
| 27000 + 888, 888, 888, 1776, 1776, 1776, 888, 888, 1776, 888, 888, 1776, 1776, 1776, 888, 888, 888, 888, 1776, 888, | ||||
| }; | ||||
| 
 | ||||
| const IrdaMessage test_decoder_rc5_expected3[] = { | ||||
|     {IrdaProtocolRC5,     0x13,      0x11,  false}, // toggle 0 | ||||
| }; | ||||
| 
 | ||||
| 
 | ||||
| /* | ||||
| _______--__--__--__----__--____--__----____----__--__--____-- | ||||
|      | 1 | 1 | 1 | 1 | 0 | 0 | 1 | 1 | 0 | 1 | 0 | 0 | 0 | 1 | | ||||
|       s1  s2   t |     address       |        command        | | ||||
| */ | ||||
| 
 | ||||
| const uint32_t test_decoder_rc5_input4[] = { | ||||
| 27000 + 888, 888, 888, 888, 888, 888, 888, 1776, 888, 888, 1776, 888, 888, 1776, 1776, 1776, 888, 888, 888, 888, 1776, 888, | ||||
| }; | ||||
| 
 | ||||
| const IrdaMessage test_decoder_rc5_expected4[] = { | ||||
|     {IrdaProtocolRC5,     0x13,      0x11,  false}, // toggle 1 | ||||
| }; | ||||
| 
 | ||||
| /* | ||||
| _______--__----____--__--__--__--__--__--__--__--__--__--__-- | ||||
|      | 1 | 1 | 0 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | | ||||
|       s1  s2   t |     address       |        command        | | ||||
| */ | ||||
| 
 | ||||
| const uint32_t test_decoder_rc5_input5[] = { | ||||
| 27000 + 888, 888, 888, 1776, 1776, 888, 888, 888, 888, 888, 888, 888, 888, 888, 888, 888, 888, 888, 888, 888, 888, 888, 888, 888, 888, 888, | ||||
| }; | ||||
| 
 | ||||
| const IrdaMessage test_decoder_rc5_expected5[] = { | ||||
|     {IrdaProtocolRC5,     0x1F,      0x3F,  false}, // toggle 0 | ||||
| }; | ||||
| 
 | ||||
| /* | ||||
| _______--__--__--__--__--__--__--__--__--__--__--__--__--__-- | ||||
|      | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | | ||||
|       s1  s2   t |     address       |        command        | | ||||
| */ | ||||
| 
 | ||||
| const uint32_t test_decoder_rc5_input6[] = { | ||||
| 27000 + 888, 888, 888, 888, 888, 888, 888, 888, 888, 888, 888, 888, 888, 888, 888, 888, 888, 888, 888, 888, 888, 888, 888, 888, 888, 888, 888, 888, | ||||
| }; | ||||
| 
 | ||||
| const IrdaMessage test_decoder_rc5_expected6[] = { | ||||
|     {IrdaProtocolRC5,     0x1F,      0x3F,  false}, // toggle 1 | ||||
| }; | ||||
| 
 | ||||
| 
 | ||||
| const uint32_t test_decoder_rc5_input_all_repeats[] = { | ||||
| 27000 + 888, 888, 888, 1776, 1776, 1776, 888, 888, 1776, 888, 888, 1776, 1776, 1776, 888, 888, 888, 888, 1776, 888, | ||||
| 27000 + 888, 888, 888, 888, 888, 888, 888, 1776, 888, 888, 1776, 888, 888, 1776, 1776, 1776, 888, 888, 888, 888, 1776, 888, | ||||
| 27000 + 888, 888, 888, 888, 888, 888, 888, 1776, 888, 888, 1776, 888, 888, 1776, 1776, 1776, 888, 888, 888, 888, 1776, 888, | ||||
| 27000 + 888, 888, 888, 888, 888, 888, 888, 1776, 888, 888, 1776, 888, 888, 1776, 1776, 1776, 888, 888, 888, 888, 1776, 888, | ||||
| 27000 + 888, 888, 888, 1776, 1776, 1776, 888, 888, 1776, 888, 888, 1776, 1776, 1776, 888, 888, 888, 888, 1776, 888, | ||||
| 27000 + 888, 888, 888, 888, 888, 888, 888, 1776, 888, 888, 1776, 888, 888, 1776, 1776, 1776, 888, 888, 888, 888, 888, 888, | ||||
| 27000 + 888, 888, 888, 1776, 1776, 1776, 888, 888, 1776, 888, 888, 1776, 1776, 1776, 888, 888, 888, 888, 888, 888, | ||||
| 27000 + 888, 888, 888, 1776, 1776, 1776, 888, 888, 1776, 888, 888, 1776, 1776, 1776, 888, 888, 888, 888, 888, 888, | ||||
| 27000 + 888, 888, 888, 888, 888, 888, 888, 888, 888, 888, 888, 888, 888, 888, 888, 888, 888, 888, 888, 888, 888, 888, 888, 888, 888, 888, 888, 888, | ||||
| 27000 + 888, 888, 888, 1776, 1776, 888, 888, 888, 888, 888, 888, 888, 888, 888, 888, 888, 888, 888, 888, 888, 888, 888, 888, 888, 888, 888, | ||||
| 27000 + 888, 888, 888, 1776, 1776, 888, 888, 888, 888, 888, 888, 888, 888, 888, 888, 888, 888, 888, 888, 888, 888, 888, 888, 888, 888, 888, | ||||
| }; | ||||
| 
 | ||||
| const IrdaMessage test_decoder_rc5_expected_all_repeats[] = { | ||||
|     {IrdaProtocolRC5,     0x13,      0x11,  false}, // toggle 0 | ||||
|     {IrdaProtocolRC5,     0x13,      0x11,  false}, // toggle 1 | ||||
|     {IrdaProtocolRC5,     0x13,      0x11,  true},  // toggle 1 | ||||
|     {IrdaProtocolRC5,     0x13,      0x11,  true},  // toggle 1 | ||||
|     {IrdaProtocolRC5,     0x13,      0x11,  false}, // toggle 0 | ||||
|     {IrdaProtocolRC5,     0x13,      0x10,  false}, // toggle 1 | ||||
|     {IrdaProtocolRC5,     0x13,      0x10,  false}, // toggle 0 | ||||
|     {IrdaProtocolRC5,     0x13,      0x10,  true},  // toggle 0 | ||||
|     {IrdaProtocolRC5,     0x1F,      0x3F,  false}, // toggle 1 | ||||
|     {IrdaProtocolRC5,     0x1F,      0x3F,  false}, // toggle 0 | ||||
|     {IrdaProtocolRC5,     0x1F,      0x3F,  true},  // toggle 0 | ||||
| }; | ||||
| 
 | ||||
| 
 | ||||
| const IrdaMessage test_rc5_all[] = { | ||||
|     {IrdaProtocolRC5,     0x1F,      0x3F,  false}, | ||||
|     {IrdaProtocolRC5,     0x00,      0x00,  false}, | ||||
|     {IrdaProtocolRC5,     0x10,      0x01,  false}, | ||||
|     {IrdaProtocolRC5,     0x01,      0x20,  false}, | ||||
|     {IrdaProtocolRC5,     0x01,      0x20,  false}, | ||||
|     {IrdaProtocolRC5,     0x01,      0x20,  true}, | ||||
|     {IrdaProtocolRC5,     0x01,      0x20,  true}, | ||||
|     {IrdaProtocolRC5,     0x01,      0x20,  true}, | ||||
|     {IrdaProtocolRC5,     0x01,      0x20,  true}, | ||||
|     {IrdaProtocolRC5,     0x1F,      0x3F,  false}, | ||||
|     {IrdaProtocolRC5,     0x0A,      0x2A,  false}, | ||||
|     {IrdaProtocolRC5,     0x15,      0x15,  false}, | ||||
|     {IrdaProtocolRC5,     0x15,      0x15,  true}, | ||||
| 
 | ||||
|     {IrdaProtocolRC5X,     0x1F,      0x3F,  false}, | ||||
|     {IrdaProtocolRC5X,     0x00,      0x00,  false}, | ||||
|     {IrdaProtocolRC5X,     0x10,      0x01,  false}, | ||||
|     {IrdaProtocolRC5X,     0x01,      0x20,  false}, | ||||
|     {IrdaProtocolRC5X,     0x01,      0x20,  false}, | ||||
|     {IrdaProtocolRC5X,     0x01,      0x20,  true}, | ||||
|     {IrdaProtocolRC5X,     0x01,      0x20,  true}, | ||||
|     {IrdaProtocolRC5X,     0x01,      0x20,  true}, | ||||
|     {IrdaProtocolRC5X,     0x01,      0x20,  true}, | ||||
|     {IrdaProtocolRC5X,     0x1F,      0x3F,  false}, | ||||
|     {IrdaProtocolRC5X,     0x0A,      0x2A,  false}, | ||||
|     {IrdaProtocolRC5X,     0x15,      0x15,  false}, | ||||
|     {IrdaProtocolRC5X,     0x15,      0x15,  true}, | ||||
| }; | ||||
| 
 | ||||
| @ -15,17 +15,17 @@ _____---------______--____--__--__------____--____--__----____--__----__--__--__ | ||||
| */ | ||||
| 
 | ||||
| const uint32_t test_decoder_rc6_input1[] = { | ||||
| 2700, 2666, 889, 444, 888, 444, 444, 444, 444, 444, 444 + 444, 888 + 444, 888, 444, 444, 888, 888, 444, 444, 888, 444, 444, 444, 444, 888, 888, 888, 444, 444, 444, 444, 444, 444, 444, 444, 444, | ||||
| 2700, 2666, 889, 444, 888, 444, 444, 444, 444, 444 + 888, 888, 444, 888, 444, 444, 888, 888, 444, 444, 888, 444, 444, 444, 444, 888, 888, 888, 444, 444, 444, 444, 444, 444, 444, 444, 444, | ||||
| 2700, 2666, 889, 444, 888, 444, 444, 444, 444, 444, 444 + 444, 888 + 444, 888, 444, 444, 888, 888, 444, 444, 888, 444, 444, 444, 444, 888, 888, 888, 444, 444, 444, 444, 444, 444, 444, 444, 888,   // failed | ||||
| 2700, 2666, 889, 444, 888, 444, 444, 444, 444, 444, 444 + 444, 888 + 444, 888, 444, 444, 888, 888, 444, 444, 888, 444, 444, 444, 444, 888, 888, 888, 444, 444, 444, 444, 444, 444, 444, 444, 444, | ||||
| 2700, 2666, 889, 444, 888, 444, 444, 444, 444, 444, 444 + 444, 888 + 444, 888, 444, 444, 888, 888, 444, 444, 888, 444, 444, 444, 444, 888, 888, 888, 444, 444, 444, 444, 444, 444, 444, 444, 444, | ||||
| 2700, 2666, 889, 444, 888, 444, 444, 444, 444, 444, 444 + 444, 888 + 444, 888, 444, 444, 888, 888, 444, 444, 888, 444, 444, 444, 444, 888, 888, 888, 444, 444, 444, 444, 444, 444, 444, 444, 444, | ||||
| 2700, 2666, 889, 444, 888, 444, 444, 444, 444, 444 + 888, 888, 444, 888, 444, 444, 888, 888, 444, 444, 888, 444, 444, 444, 444, 888, 888, 888, 444, 444, 444, 444, 444, 444, 444, 444, 444, | ||||
| 2700, 2666, 889, 444, 888, 444, 444, 444, 444, 444, 444 + 444, 888 + 444, 888, 444, 444, 888, 888, 444, 444, 888, 444, 444, 444, 444, 888, 888, 888, 444, 444, 444, 444, 444, 444, 444, 444, 888,   // failed | ||||
| 2700, 2666, 889, 444, 888, 444, 444, 444, 444, 444, 444 + 444, 888 + 444, 888, 444, 444, 888, 888, 444, 444, 888, 444, 444, 444, 444, 888, 888, 888, 444, 444, 444, 444, 444, 444, 444, 444, 444, | ||||
| 2700, 2666, 889, 444, 888, 444, 444, 444, 444, 444 + 888, 888, 444, 888, 444, 444, 888, 888, 444, 444, 888, 444, 444, 444, 444, 888, 888, 888, 444, 444, 444, 444, 444, 444, 888, | ||||
| 2700, 2666, 889, 444, 888, 444, 444, 444, 444, 444, 444 + 444, 888 + 444, 888, 444, 444, 888, 888, 444, 444, 888, 444, 444, 444, 444, 888, 888, 888, 444, 444, 444, 444, 444, 444, 444, 444, 888,   // failed | ||||
| 27000, 2666, 889, 444, 888, 444, 444, 444, 444, 444, 444 + 444, 888 + 444, 888, 444, 444, 888, 888, 444, 444, 888, 444, 444, 444, 444, 888, 888, 888, 444, 444, 444, 444, 444, 444, 444, 444, 444, | ||||
| 27000, 2666, 889, 444, 888, 444, 444, 444, 444, 444 + 888, 888, 444, 888, 444, 444, 888, 888, 444, 444, 888, 444, 444, 444, 444, 888, 888, 888, 444, 444, 444, 444, 444, 444, 444, 444, 444, | ||||
| 27000, 2666, 889, 444, 888, 444, 444, 444, 444, 444, 444 + 444, 888 + 444, 888, 444, 444, 888, 888, 444, 444, 888, 444, 444, 444, 444, 888, 888, 888, 444, 444, 444, 444, 444, 444, 444, 444, 888,   // failed | ||||
| 27000, 2666, 889, 444, 888, 444, 444, 444, 444, 444, 444 + 444, 888 + 444, 888, 444, 444, 888, 888, 444, 444, 888, 444, 444, 444, 444, 888, 888, 888, 444, 444, 444, 444, 444, 444, 444, 444, 444, | ||||
| 27000, 2666, 889, 444, 888, 444, 444, 444, 444, 444, 444 + 444, 888 + 444, 888, 444, 444, 888, 888, 444, 444, 888, 444, 444, 444, 444, 888, 888, 888, 444, 444, 444, 444, 444, 444, 444, 444, 444, | ||||
| 27000, 2666, 889, 444, 888, 444, 444, 444, 444, 444, 444 + 444, 888 + 444, 888, 444, 444, 888, 888, 444, 444, 888, 444, 444, 444, 444, 888, 888, 888, 444, 444, 444, 444, 444, 444, 444, 444, 444, | ||||
| 27000, 2666, 889, 444, 888, 444, 444, 444, 444, 444 + 888, 888, 444, 888, 444, 444, 888, 888, 444, 444, 888, 444, 444, 444, 444, 888, 888, 888, 444, 444, 444, 444, 444, 444, 444, 444, 444, | ||||
| 27000, 2666, 889, 444, 888, 444, 444, 444, 444, 444, 444 + 444, 888 + 444, 888, 444, 444, 888, 888, 444, 444, 888, 444, 444, 444, 444, 888, 888, 888, 444, 444, 444, 444, 444, 444, 444, 444, 888,   // failed | ||||
| 27000, 2666, 889, 444, 888, 444, 444, 444, 444, 444, 444 + 444, 888 + 444, 888, 444, 444, 888, 888, 444, 444, 888, 444, 444, 444, 444, 888, 888, 888, 444, 444, 444, 444, 444, 444, 444, 444, 444, | ||||
| 27000, 2666, 889, 444, 888, 444, 444, 444, 444, 444 + 888, 888, 444, 888, 444, 444, 888, 888, 444, 444, 888, 444, 444, 444, 444, 888, 888, 888, 444, 444, 444, 444, 444, 444, 888, | ||||
| 27000, 2666, 889, 444, 888, 444, 444, 444, 444, 444, 444 + 444, 888 + 444, 888, 444, 444, 888, 888, 444, 444, 888, 444, 444, 444, 444, 888, 888, 888, 444, 444, 444, 444, 444, 444, 444, 444, 888,   // failed | ||||
| }; | ||||
| 
 | ||||
| const IrdaMessage test_decoder_rc6_expected1[] = { | ||||
| @ -54,14 +54,14 @@ const IrdaMessage test_encoder_rc6_input1[] = { | ||||
| }; | ||||
| 
 | ||||
| const uint32_t test_encoder_rc6_expected1[] = { | ||||
| 2700, 2666, 889, 444, 888, 444, 444, 444, 444, 444, 888, 888+444, 888, 444, 444, 888, 888, 444, 444, 888, 444, 444, 444, 444, 888, 888, 888, 444, 444, 444, 444, 444, 444, 444, 444, 444, | ||||
| 2700, 2666, 889, 444, 888, 444, 444, 444, 444, 444, 888, 888+444, 888, 444, 444, 888, 888, 444, 444, 888, 444, 444, 444, 444, 888, 888, 888, 444, 444, 444, 444, 444, 444, 444, 444, 444, | ||||
| 2700, 2666, 889, 444, 888, 444, 444, 444, 444, 444+888, 888, 444, 888, 444, 444, 888, 888, 444, 444, 888, 444, 444, 444, 444, 888, 888, 888, 444, 444, 444, 444, 444, 444, 888, | ||||
| 2700, 2666, 889, 444, 888, 444, 444, 444, 444, 444+888, 888, 444, 888, 444, 444, 888, 888, 444, 444, 888, 444, 444, 444, 444, 888, 888, 888, 444, 444, 444, 444, 444, 444, 888, | ||||
| 2700, 2666, 889, 444, 888, 444, 444, 444, 444, 444+888, 888, 444, 888, 444, 444, 888, 888, 444, 444, 888, 444, 444, 444, 444, 888, 888, 888, 444, 444, 444, 444, 444, 444, 888, | ||||
| 2700, 2666, 889, 444, 888, 444, 444, 444, 444, 444, 888, 888+444, 888, 444, 444, 888, 888, 444, 444, 888, 444, 444, 444, 444, 888, 888, 888, 444, 444, 444, 444, 444, 444, 444, 444, 444, | ||||
| 2700, 2666, 889, 444, 888, 444, 444, 444, 444, 444+888, 888, 444, 888, 444, 444, 888, 888, 444, 444, 888, 444, 444, 444, 444, 888, 888, 888, 444, 444, 444, 444, 444, 444, 444, 444, 444, | ||||
| 2700, 2666, 889, 444, 888, 444, 444, 444, 444, 444+888, 888, 444, 888, 444, 444, 888, 888, 444, 444, 888, 444, 444, 444, 444, 888, 888, 888, 444, 444, 444, 444, 444, 444, 444, 444, 444, | ||||
| 27000, 2666, 889, 444, 888, 444, 444, 444, 444, 444, 888, 888+444, 888, 444, 444, 888, 888, 444, 444, 888, 444, 444, 444, 444, 888, 888, 888, 444, 444, 444, 444, 444, 444, 444, 444, 444, | ||||
| 27000, 2666, 889, 444, 888, 444, 444, 444, 444, 444, 888, 888+444, 888, 444, 444, 888, 888, 444, 444, 888, 444, 444, 444, 444, 888, 888, 888, 444, 444, 444, 444, 444, 444, 444, 444, 444, | ||||
| 27000, 2666, 889, 444, 888, 444, 444, 444, 444, 444+888, 888, 444, 888, 444, 444, 888, 888, 444, 444, 888, 444, 444, 444, 444, 888, 888, 888, 444, 444, 444, 444, 444, 444, 888, | ||||
| 27000, 2666, 889, 444, 888, 444, 444, 444, 444, 444+888, 888, 444, 888, 444, 444, 888, 888, 444, 444, 888, 444, 444, 444, 444, 888, 888, 888, 444, 444, 444, 444, 444, 444, 888, | ||||
| 27000, 2666, 889, 444, 888, 444, 444, 444, 444, 444+888, 888, 444, 888, 444, 444, 888, 888, 444, 444, 888, 444, 444, 444, 444, 888, 888, 888, 444, 444, 444, 444, 444, 444, 888, | ||||
| 27000, 2666, 889, 444, 888, 444, 444, 444, 444, 444, 888, 888+444, 888, 444, 444, 888, 888, 444, 444, 888, 444, 444, 444, 444, 888, 888, 888, 444, 444, 444, 444, 444, 444, 444, 444, 444, | ||||
| 27000, 2666, 889, 444, 888, 444, 444, 444, 444, 444+888, 888, 444, 888, 444, 444, 888, 888, 444, 444, 888, 444, 444, 444, 444, 888, 888, 888, 444, 444, 444, 444, 444, 444, 444, 444, 444, | ||||
| 27000, 2666, 889, 444, 888, 444, 444, 444, 444, 444+888, 888, 444, 888, 444, 444, 888, 888, 444, 444, 888, 444, 444, 444, 444, 888, 888, 888, 444, 444, 444, 444, 444, 444, 444, 444, 444, | ||||
| }; | ||||
| 
 | ||||
| 
 | ||||
|  | ||||
| @ -53,6 +53,9 @@ const uint8_t *_I_ButtonLeft_4x7[] = {_I_ButtonLeft_4x7_0}; | ||||
| const uint8_t _I_ButtonLeftSmall_3x5_0[] = {0x04,0x06,0x07,0x06,0x04,}; | ||||
| const uint8_t *_I_ButtonLeftSmall_3x5[] = {_I_ButtonLeftSmall_3x5_0}; | ||||
| 
 | ||||
| const uint8_t _I_DFU_128x50_0[] = {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0x07,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xF0,0x00,0x38,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0E,0x00,0xC0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x8F,0x01,0x00,0x00,0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x80,0x7F,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xC0,0xFF,0x00,0x00,0x00,0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xE0,0xFF,0x00,0x00,0x00,0x10,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xE0,0xFF,0x00,0x00,0x00,0x20,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xE0,0x75,0x00,0x00,0xF0,0x3F,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x80,0xFF,0x0A,0x00,0x00,0x0F,0x60,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x60,0xE0,0x0F,0x00,0xC0,0xE0,0x4F,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x18,0x00,0x18,0x00,0x30,0x1E,0x90,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x00,0x18,0x00,0x8C,0x01,0xA0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x80,0x81,0xFF,0x19,0x00,0x63,0x00,0xC0,0xF0,0x07,0x00,0x00,0x00,0x00,0x00,0x00,0x40,0x60,0x5E,0x1F,0x80,0x18,0x00,0xE0,0x0E,0x18,0x00,0x00,0x00,0x00,0x00,0x00,0x20,0x18,0xAF,0x0F,0x40,0x06,0x00,0xF8,0x01,0x20,0x00,0x00,0x00,0x00,0x00,0x00,0x18,0x06,0x57,0x01,0x20,0x01,0x00,0x78,0x00,0x3E,0x00,0x00,0x00,0x00,0x00,0x00,0x84,0x81,0xAF,0x02,0x90,0x00,0x00,0x38,0x80,0x41,0x00,0x00,0x00,0x00,0x00,0x00,0x63,0x80,0x57,0x01,0x48,0x00,0x00,0x10,0x60,0x40,0x00,0x00,0x00,0x00,0x00,0xC0,0x10,0x80,0xAB,0x00,0x24,0x00,0x00,0x08,0x10,0x40,0x3F,0x00,0x00,0x00,0x00,0x38,0x0C,0xC0,0x57,0x01,0x12,0x00,0x00,0x04,0x08,0x40,0xC0,0x0F,0x00,0x00,0xC0,0x07,0x03,0xF0,0xAB,0x00,0x0A,0x00,0x00,0x02,0x04,0x40,0x00,0xF0,0x1F,0x80,0x3F,0xC0,0x00,0xFC,0x55,0x01,0x05,0xE0,0x00,0x01,0x04,0x40,0x00,0x00,0xE0,0x7F,0x00,0x30,0x00,0xFF,0xAB,0x00,0x05,0xE0,0x80,0x00,0x02,0x40,0x0F,0x00,0x00,0x00,0x80,0x0F,0xE0,0xCF,0x55,0x81,0x02,0xF0,0x40,0x00,0x02,0x40,0xF0,0x0F,0x00,0x00,0x7F,0x00,0xFE,0xC3,0xAB,0x80,0x02,0x78,0x20,0x00,0x01,0x40,0x00,0xF0,0xFF,0xFF,0x00,0xF0,0xFF,0xC0,0xD5,0x81,0x01,0x7E,0x10,0x80,0x00,0x20,0x00,0x00,0x00,0x00,0xC0,0xFF,0x0F,0xE0,0xFA,0x83,0xC1,0x3F,0x08,0x80,0x00,0x20,0x00,0x00,0x00,0x00,0xFF,0xFF,0x01,0xD8,0x07,0x83,0xF1,0x1F,0x04,0x40,0x00,0x20,0x00,0xE0,0xFF,0xFF,0xFF,0x0F,0x80,0xC7,0x01,0x83,0xF1,0x0F,0x00,0x20,0x00,0x10,0xE0,0xFF,0xFF,0xFF,0x3F,0xC0,0x7F,0x40,0x80,0x83,0xE1,0x01,0x00,0x20,0x00,0x18,0xFC,0xFF,0xFF,0xFF,0x03,0x3F,0x00,0x20,0xFC,0x83,0x01,0x00,0x00,0x10,0x00,0x18,0xFF,0xFF,0xFF,0x3F,0xF0,0x00,0x00,0x10,0xD7,0x01,0x03,0x00,0x00,0x08,0x00,0x1C,0xFF,0xFF,0x01,0x00,0x0F,0x00,0x00,0x88,0xAB,0x02,0xE3,0x01,0x00,0x08,0x00,0x0C,0xFF,0x07,0x00,0xE0,0x00,0x00,0x00,0xC4,0x55,0x05,0x1E,0x00,0x00,0x04,0x00,0x0E,0x7F,0x00,0x00,0x1C,0x00,0x00,0x00,0xA3,0xAB,0x02,0x06,0x00,0x00,0x02,0x00,0x0F,0x0F,0x00,0x80,0x03,0x00,0x00,0xC0,0x10,0x57,0x05,0x02,0x00,0x00,0x01,0x80,0x07,0x03,0x00,0x70,0x00,0x00,0x00,0x30,0x08,0xAB,0x0A,0x02,0x00,0xC0,0x00,0xC0,0x07,0x00,0x00,0x0C,0x00,0x00,0x00,0x0C,0x84,0x57,0x15,0x01,0x00,0x30,0x00,0xE0,0x07,0x00,0x00,0x03,0x00,0x00,0x00,0x03,0xC3,0xFF,0x2A,0x01,0x00,0x0C,0x00,0xF0,0x0F,0x00,0xC0,0x00,0x00,0x00,0xE0,0xC0,0xE0,0xFE,0x55,0x01,0x82,0x03,0x00,0xF8,0x15,0x00,0x30,0x00,0x00,0x00,0x1C,0x30,0x78,0xFE,0xAA,0x01,0x7C,0x00,0x00,0xFC,0x23,0x00,0x0E,0x00,0x00,0xC0,0x03,0x0C,0x3C,0x7F,0x5D,0x01,0x00,0x00,0x00,0xFF,0x45,0xC0,0x01,0x00,0x00,0x3E,0x00,0x02,0x8F,0xBF,0xAE,0x03,0x00,0x00,0xC0,0xFF,0x82,0x30,0x00,0x00,0xC0,0x01,0x80,0xC1,0x43,0xFE,0x5D,0x01,0x00,0x00,0xF0,0xFF,0x05,0x0F,0x00,0x80,0x3F,0x00,0x60,0xF0,0x31,0xF6,0xAE,0x03,0x00,0x00,0xFA,0xAF,0x02,0xFC,0xFF,0x7F,0x00,0x00,0x18,0x7C,0x08,0x23,0xFF,0x05,0x00,0x00,0xFD,0x55,0x01,0x00,0x00,0x00,0x00,0x00,0x86,0x1F,0x84,0x30,0xFE,0x0A,0x00,0x00,0xAA,0xAA,0x00,0x00,0x00,0x00,0x00,0x80,0xF1,0x07,0x43,0x18,0xFF,0x15,0x00,0x00,0x54,0x15,0x00,0x00,0x00,0x00,0x00,0xF8,0xFF,0x80,0x20,0x8C,0xFF,0xAA,0x00,0x00,0x00,0x00,0x00,}; | ||||
| const uint8_t *_I_DFU_128x50[] = {_I_DFU_128x50_0}; | ||||
| 
 | ||||
| const uint8_t _I_Warning_30x23_0[] = {0x00,0xC0,0x00,0x00,0x00,0xE0,0x01,0x00,0x00,0xF0,0x03,0x00,0x00,0xF0,0x03,0x00,0x00,0xF8,0x07,0x00,0x00,0x3C,0x0F,0x00,0x00,0x3C,0x0F,0x00,0x00,0x3E,0x1F,0x00,0x00,0x3F,0x3F,0x00,0x00,0x3F,0x3F,0x00,0x80,0x3F,0x7F,0x00,0xC0,0x3F,0xFF,0x00,0xC0,0x3F,0xFF,0x00,0xE0,0x3F,0xFF,0x01,0xF0,0x3F,0xFF,0x03,0xF0,0x3F,0xFF,0x03,0xF8,0x3F,0xFF,0x07,0xFC,0xFF,0xFF,0x0F,0xFC,0xFF,0xFF,0x0F,0xFE,0x3F,0xFF,0x1F,0xFF,0x3F,0xFF,0x3F,0xFF,0xFF,0xFF,0x3F,0xFE,0xFF,0xFF,0x1F,}; | ||||
| const uint8_t *_I_Warning_30x23[] = {_I_Warning_30x23_0}; | ||||
| 
 | ||||
| @ -95,6 +98,18 @@ const uint8_t *_I_Flipper_young_80x60[] = {_I_Flipper_young_80x60_0}; | ||||
| const uint8_t _I_DolphinFirstStart3_57x48_0[] = {0x00,0x00,0x00,0x80,0xFF,0x07,0x00,0x00,0x00,0x00,0x00,0x60,0x00,0x38,0x00,0x00,0x00,0x00,0x00,0x18,0x00,0xC0,0x00,0x00,0x00,0x00,0x00,0x04,0x00,0x00,0x01,0x00,0x00,0x00,0x00,0x02,0x00,0x00,0x02,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x04,0x00,0x00,0xF8,0x03,0x01,0x00,0x00,0x08,0x00,0x00,0x04,0xBC,0x00,0x00,0x00,0x10,0x00,0x00,0x02,0xC0,0x00,0x00,0x00,0x20,0x00,0x00,0x02,0x00,0x01,0x00,0x00,0x20,0x00,0x00,0x02,0x00,0x02,0x00,0x38,0x40,0x00,0x00,0x02,0x00,0x04,0x00,0x3E,0x40,0x00,0x00,0xF4,0x03,0x08,0x80,0x07,0x80,0x00,0x00,0x5C,0x0D,0x10,0xE0,0x01,0x80,0x00,0x00,0xA8,0x3A,0x20,0xE0,0x00,0x00,0x01,0x00,0x58,0x55,0x00,0xC0,0x01,0x00,0x01,0x00,0xB0,0xAA,0x00,0x80,0x07,0x00,0x01,0x00,0x60,0x55,0x01,0x00,0x1E,0x00,0x01,0x0E,0xC0,0xAA,0x02,0xE0,0x5C,0x00,0x01,0x11,0x80,0x55,0x05,0x00,0xA9,0x00,0x01,0x21,0x00,0xAB,0x0A,0x00,0x56,0x07,0x01,0x41,0x00,0x56,0x15,0x00,0xEC,0x08,0x01,0x81,0x00,0xBF,0x2A,0x00,0x34,0x08,0x01,0x01,0xF1,0xC0,0x57,0x00,0x0C,0x08,0x01,0x02,0x0A,0x00,0xBE,0x00,0x04,0x08,0x01,0x02,0x06,0x00,0x78,0x83,0x02,0x04,0x01,0x02,0x0C,0x00,0xF0,0x7F,0x01,0x04,0x01,0x02,0xF4,0x01,0xFE,0x81,0x00,0x04,0x01,0x04,0x08,0xFF,0x6B,0x40,0x00,0x02,0x01,0x04,0x88,0x55,0x1D,0x40,0x00,0x02,0x01,0x04,0x50,0xAA,0x06,0x20,0x00,0x02,0x01,0x04,0x30,0xD4,0x01,0x20,0x00,0x01,0x01,0x04,0x10,0x68,0x00,0x10,0x00,0x01,0x01,0x04,0x18,0x18,0x00,0x10,0x00,0x01,0x01,0x08,0x18,0x06,0x80,0x10,0x00,0x01,0x01,0x08,0xE8,0x01,0x60,0x08,0x80,0x00,0x01,0x08,0x08,0x00,0x18,0x08,0x80,0x00,0x00,0x08,0x10,0x00,0x06,0x08,0x80,0x00,0x00,0x08,0x60,0xE0,0x01,0x08,0x80,0x00,0x00,0x08,0x80,0x1F,0x00,0x08,0x80,0x00,0x00,0x08,0x80,0x04,0x00,0x04,0x00,0x01,0x00,0x08,0x80,0x04,0x00,0x04,0x00,0x01,0x00,0x10,0x00,0x03,0x00,0x04,0x00,0x01,0x00,0x10,0x00,0x03,0x00,0x04,0x00,0x01,0x00,0x10,0x00,0x01,0x00,0x04,0x00,0x02,0x00,0x10,0x00,0x01,0x00,0x04,0x00,0x02,0x00,0x10,0x80,0x00,0x00,0x04,0x00,0x02,0x00,0x10,0x80,0x00,0x00,0x04,0x00,0x06,0x00,}; | ||||
| const uint8_t *_I_DolphinFirstStart3_57x48[] = {_I_DolphinFirstStart3_57x48_0}; | ||||
| 
 | ||||
| const uint8_t _I_Scanning_123x52_0[] = {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xC0,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xF0,0x00,0x07,0x00,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xAC,0x03,0x18,0x00,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x00,0x10,0x00,0x00,0x00,0x56,0x05,0x60,0x00,0x00,0x00,0x80,0x02,0x00,0x00,0x00,0x00,0x08,0x00,0x00,0x00,0x81,0x0A,0x80,0x00,0x00,0x00,0x80,0x02,0x00,0x00,0x00,0x00,0x04,0x00,0x00,0x80,0x00,0x15,0x00,0x01,0x00,0x00,0x40,0x02,0x00,0x00,0x00,0x00,0x02,0x00,0x00,0x40,0x00,0x38,0x00,0x02,0x00,0x00,0x40,0x02,0x00,0x00,0x00,0x00,0x82,0x00,0x00,0x20,0x00,0x74,0x00,0x04,0x00,0x00,0x40,0x82,0x01,0x00,0x00,0x00,0x41,0x00,0x00,0x20,0x00,0x68,0x00,0x04,0x00,0x00,0x20,0x82,0x02,0x06,0x00,0x00,0x21,0x00,0x00,0x10,0x00,0xD0,0xE0,0x0F,0x00,0x00,0x20,0x82,0x02,0x0A,0x0C,0x80,0x20,0x08,0x00,0x10,0x00,0xA0,0x1C,0x10,0x00,0x00,0x20,0x82,0x02,0x0A,0x14,0x80,0x10,0x04,0x00,0x08,0xE0,0xD3,0x03,0x10,0x00,0x00,0x10,0x82,0x02,0x0A,0x14,0x80,0x10,0x02,0x00,0x08,0x90,0xA7,0x40,0x24,0x00,0x00,0x10,0x82,0x02,0x0A,0x14,0x80,0x10,0x02,0x00,0x08,0xC8,0x7F,0x84,0x28,0x00,0x00,0x10,0x84,0x02,0x0A,0xFF,0x80,0x10,0x02,0x00,0x88,0x67,0x3E,0x88,0x28,0x00,0x00,0x10,0x84,0xFA,0xFF,0xFF,0x80,0x10,0x02,0x00,0x44,0x64,0x2E,0x88,0x28,0x00,0x00,0x10,0xFC,0xAF,0xFF,0x15,0x80,0x10,0x04,0x00,0x44,0xE4,0x2F,0x88,0x2A,0x00,0x00,0x18,0xD4,0xDF,0x1F,0x14,0x80,0x20,0x08,0x00,0x44,0xE4,0x2F,0x50,0xFF,0x00,0xFE,0x1F,0xEC,0x3F,0x0A,0x14,0x00,0x21,0x00,0x00,0x44,0xC4,0x2F,0xEA,0x00,0x01,0x01,0x1A,0xFC,0x02,0x0A,0x14,0x00,0x41,0x00,0x00,0x84,0x88,0x2F,0x1D,0x00,0x82,0x7D,0x1E,0x84,0x02,0x0A,0x18,0x00,0x82,0x00,0x00,0x86,0x1F,0xC6,0x06,0x00,0x84,0x7D,0x16,0x84,0x02,0x0A,0x00,0x00,0x02,0x00,0x00,0x46,0xF5,0xC3,0x01,0x00,0x44,0x01,0x22,0x84,0x02,0x0C,0x00,0x00,0x04,0x00,0x00,0x87,0x0A,0x7C,0x00,0x00,0x44,0x03,0x22,0x88,0x02,0x00,0x00,0x00,0x08,0x00,0x00,0x45,0x05,0x08,0x00,0x7E,0xA4,0x03,0x42,0x88,0x02,0x00,0x00,0x00,0x10,0x00,0x00,0x86,0x06,0x00,0xC0,0x81,0xA5,0x07,0x42,0x08,0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x05,0x00,0x00,0x30,0x00,0xD2,0xFF,0x81,0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x00,0x00,0x0C,0x00,0xD2,0x1F,0x80,0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x05,0x80,0x00,0x03,0x00,0xD1,0x1F,0x00,0x09,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x00,0xE1,0x00,0x80,0xE9,0x0F,0x00,0x12,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x05,0x00,0x1E,0x00,0xC0,0xE8,0x0F,0x00,0x14,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x70,0xEE,0x0F,0x00,0x18,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x07,0x00,0x00,0x00,0x3C,0xF9,0x0F,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x00,0x00,0xAA,0x9F,0xF0,0x0F,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x40,0x55,0xFD,0x5F,0xF0,0x17,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x80,0xEA,0xFF,0x3F,0xE0,0x17,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x40,0xD5,0xFF,0x1F,0xE0,0x17,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x80,0xAA,0xFF,0x0F,0xE0,0x13,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x55,0x55,0x03,0xF0,0x15,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x00,0xAA,0xAA,0x00,0xB0,0x0A,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x54,0x75,0x00,0x58,0x0D,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x00,0xA8,0x0F,0x00,0xA8,0x06,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x05,0x00,0x7C,0x00,0x00,0x5C,0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x00,0xAE,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x05,0x00,0x00,0x00,0x00,0xD7,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0A,0x00,0x00,0x00,0x80,0x7B,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x15,0x00,0x00,0x00,0xC0,0x1F,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x2A,0x00,0x00,0x00,0xF0,0x07,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x55,0x00,0x00,0x00,0xFC,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xAA,0x00,0x00,0x00,0x1F,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,}; | ||||
| const uint8_t *_I_Scanning_123x52[] = {_I_Scanning_123x52_0}; | ||||
| 
 | ||||
| const uint8_t _I_Quest_7x8_0[] = {0x1E,0x33,0x33,0x30,0x18,0x0C,0x00,0x0C,}; | ||||
| const uint8_t *_I_Quest_7x8[] = {_I_Quest_7x8_0}; | ||||
| 
 | ||||
| const uint8_t _I_Unlock_7x8_0[] = {0x1C,0x22,0x02,0x4F,0x67,0x73,0x79,0x3C,}; | ||||
| const uint8_t *_I_Unlock_7x8[] = {_I_Unlock_7x8_0}; | ||||
| 
 | ||||
| const uint8_t _I_Lock_7x8_0[] = {0x1C,0x22,0x22,0x7F,0x7F,0x77,0x7F,0x3E,}; | ||||
| const uint8_t *_I_Lock_7x8[] = {_I_Lock_7x8_0}; | ||||
| 
 | ||||
| const uint8_t _I_PassportBottom_128x17_0[] = {0x2C,0x00,0x00,0x00,0x00,0x00,0x00,0xF0,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x8F,0x34,0x00,0x00,0x00,0x00,0x00,0x00,0x58,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x95,0x2C,0x00,0x00,0x00,0x00,0x00,0x00,0xA8,0xAA,0xAA,0xAA,0xAA,0xAA,0xAA,0xAA,0x9A,0x38,0x00,0x00,0x00,0x00,0x00,0x00,0x58,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x95,0x30,0x00,0x00,0x00,0x00,0x00,0x00,0xA8,0xAA,0xAA,0xAA,0xAA,0xAA,0xAA,0xAA,0x9A,0x38,0x00,0x00,0x00,0x00,0x00,0x00,0x58,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x95,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0xA8,0xAA,0xAA,0xAA,0xAA,0xAA,0xAA,0xAA,0x9A,0xF2,0x00,0x00,0x00,0x00,0x00,0x00,0xF0,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x8F,0xF9,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x80,0x0D,0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xC0,0x05,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x60,0x05,0xF2,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x3F,0x05,0xFA,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x1F,0x09,0x79,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0xD5,0x80,0x55,0xD5,0x00,0xF3,0xCC,0xAA,0xAA,0xAA,0xAA,0xAA,0xAA,0xAA,0xAA,0xAA,0x6A,0x00,0xAB,0x6A,0x00,0x06,0x86,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x3F,0x00,0xFE,0x3F,0x00,0xFC,0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,}; | ||||
| const uint8_t *_I_PassportBottom_128x17[] = {_I_PassportBottom_128x17_0}; | ||||
| 
 | ||||
| @ -321,12 +336,12 @@ const uint8_t _A_iButton_14_5[] = {0x00,0x00,0x00,0x38,0x00,0x24,0x00,0x22,0x80, | ||||
| const uint8_t _A_iButton_14_6[] = {0x00,0x00,0x00,0x38,0x00,0x24,0x00,0x23,0x80,0x20,0xF0,0x10,0x0C,0x0D,0xE2,0x02,0x91,0x01,0x69,0x01,0x15,0x01,0x8D,0x00,0x4D,0x00,0x3E,0x00,}; | ||||
| const uint8_t *_A_iButton_14[] = {_A_iButton_14_0,_A_iButton_14_1,_A_iButton_14_2,_A_iButton_14_3,_A_iButton_14_4,_A_iButton_14_5,_A_iButton_14_6}; | ||||
| 
 | ||||
| const uint8_t _I_Detailed_chip_17x13_0[] = {0xFC,0x7F,0x00,0x02,0x80,0x00,0x11,0x11,0x01,0x91,0x12,0x01,0x5F,0xF0,0x01,0x21,0x08,0x01,0x21,0x08,0x01,0x21,0x08,0x01,0x5F,0xF4,0x01,0x91,0x12,0x01,0x11,0x11,0x01,0x02,0x80,0x00,0xFC,0x7F,0x00,}; | ||||
| const uint8_t *_I_Detailed_chip_17x13[] = {_I_Detailed_chip_17x13_0}; | ||||
| 
 | ||||
| const uint8_t _I_Medium_chip_22x21_0[] = {0xFC,0xFF,0x0F,0x02,0x00,0x10,0xF9,0xFF,0x27,0x85,0x52,0x28,0xC5,0xFF,0x28,0x25,0x00,0x29,0x95,0x67,0x2A,0x5D,0x60,0x2E,0x55,0x00,0x2A,0x1D,0x80,0x2E,0x55,0x80,0x2A,0x1D,0x80,0x2E,0x55,0x80,0x2A,0x5D,0x80,0x2E,0x95,0x7D,0x2A,0x25,0x00,0x29,0xC5,0xFF,0x28,0x85,0x52,0x28,0xF9,0xFF,0x27,0x02,0x00,0x10,0xFC,0xFF,0x0F,}; | ||||
| const uint8_t *_I_Medium_chip_22x21[] = {_I_Medium_chip_22x21_0}; | ||||
| 
 | ||||
| const uint8_t _I_EMV_Chip_14x11_0[] = {0xFC,0x0F,0x02,0x10,0xC9,0x24,0x2F,0x3C,0x11,0x22,0x11,0x22,0x11,0x22,0x2F,0x3D,0xC9,0x24,0x02,0x10,0xFC,0x0F,}; | ||||
| const uint8_t *_I_EMV_Chip_14x11[] = {_I_EMV_Chip_14x11_0}; | ||||
| 
 | ||||
| const uint8_t _I_Health_16x16_0[] = {0x00,0x00,0x00,0x00,0x00,0x00,0xC0,0x01,0x40,0x01,0x40,0x01,0x78,0x0F,0x08,0x08,0x78,0x0F,0x40,0x01,0x40,0x01,0xC0,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,}; | ||||
| const uint8_t *_I_Health_16x16[] = {_I_Health_16x16_0}; | ||||
| 
 | ||||
| @ -442,6 +457,7 @@ const Icon I_125_10px = {.width=10,.height=10,.frame_count=1,.frame_rate=0,.fram | ||||
| const Icon I_ButtonRightSmall_3x5 = {.width=3,.height=5,.frame_count=1,.frame_rate=0,.frames=_I_ButtonRightSmall_3x5}; | ||||
| const Icon I_ButtonLeft_4x7 = {.width=4,.height=7,.frame_count=1,.frame_rate=0,.frames=_I_ButtonLeft_4x7}; | ||||
| const Icon I_ButtonLeftSmall_3x5 = {.width=3,.height=5,.frame_count=1,.frame_rate=0,.frames=_I_ButtonLeftSmall_3x5}; | ||||
| const Icon I_DFU_128x50 = {.width=128,.height=50,.frame_count=1,.frame_rate=0,.frames=_I_DFU_128x50}; | ||||
| const Icon I_Warning_30x23 = {.width=30,.height=23,.frame_count=1,.frame_rate=0,.frames=_I_Warning_30x23}; | ||||
| const Icon I_ButtonRight_4x7 = {.width=4,.height=7,.frame_count=1,.frame_rate=0,.frames=_I_ButtonRight_4x7}; | ||||
| const Icon I_ButtonCenter_7x7 = {.width=7,.height=7,.frame_count=1,.frame_rate=0,.frames=_I_ButtonCenter_7x7}; | ||||
| @ -456,6 +472,10 @@ const Icon I_DolphinFirstStart8_56x51 = {.width=56,.height=51,.frame_count=1,.fr | ||||
| const Icon I_DolphinFirstStart7_61x51 = {.width=61,.height=51,.frame_count=1,.frame_rate=0,.frames=_I_DolphinFirstStart7_61x51}; | ||||
| const Icon I_Flipper_young_80x60 = {.width=80,.height=60,.frame_count=1,.frame_rate=0,.frames=_I_Flipper_young_80x60}; | ||||
| const Icon I_DolphinFirstStart3_57x48 = {.width=57,.height=48,.frame_count=1,.frame_rate=0,.frames=_I_DolphinFirstStart3_57x48}; | ||||
| const Icon I_Scanning_123x52 = {.width=123,.height=52,.frame_count=1,.frame_rate=0,.frames=_I_Scanning_123x52}; | ||||
| const Icon I_Quest_7x8 = {.width=7,.height=8,.frame_count=1,.frame_rate=0,.frames=_I_Quest_7x8}; | ||||
| const Icon I_Unlock_7x8 = {.width=7,.height=8,.frame_count=1,.frame_rate=0,.frames=_I_Unlock_7x8}; | ||||
| const Icon I_Lock_7x8 = {.width=7,.height=8,.frame_count=1,.frame_rate=0,.frames=_I_Lock_7x8}; | ||||
| const Icon I_PassportBottom_128x17 = {.width=128,.height=17,.frame_count=1,.frame_rate=0,.frames=_I_PassportBottom_128x17}; | ||||
| const Icon I_DoorLeft_8x56 = {.width=8,.height=56,.frame_count=1,.frame_rate=0,.frames=_I_DoorLeft_8x56}; | ||||
| const Icon I_DoorLocked_10x56 = {.width=10,.height=56,.frame_count=1,.frame_rate=0,.frames=_I_DoorLocked_10x56}; | ||||
| @ -503,8 +523,8 @@ const Icon A_Sub1ghz_14 = {.width=14,.height=14,.frame_count=6,.frame_rate=3,.fr | ||||
| const Icon A_Tamagotchi_14 = {.width=14,.height=14,.frame_count=6,.frame_rate=3,.frames=_A_Tamagotchi_14}; | ||||
| const Icon A_U2F_14 = {.width=14,.height=14,.frame_count=4,.frame_rate=3,.frames=_A_U2F_14}; | ||||
| const Icon A_iButton_14 = {.width=14,.height=14,.frame_count=7,.frame_rate=3,.frames=_A_iButton_14}; | ||||
| const Icon I_Detailed_chip_17x13 = {.width=17,.height=13,.frame_count=1,.frame_rate=0,.frames=_I_Detailed_chip_17x13}; | ||||
| const Icon I_Medium_chip_22x21 = {.width=22,.height=21,.frame_count=1,.frame_rate=0,.frames=_I_Medium_chip_22x21}; | ||||
| const Icon I_EMV_Chip_14x11 = {.width=14,.height=11,.frame_count=1,.frame_rate=0,.frames=_I_EMV_Chip_14x11}; | ||||
| const Icon I_Health_16x16 = {.width=16,.height=16,.frame_count=1,.frame_rate=0,.frames=_I_Health_16x16}; | ||||
| const Icon I_FaceCharging_29x14 = {.width=29,.height=14,.frame_count=1,.frame_rate=0,.frames=_I_FaceCharging_29x14}; | ||||
| const Icon I_BatteryBody_52x28 = {.width=52,.height=28,.frame_count=1,.frame_rate=0,.frames=_I_BatteryBody_52x28}; | ||||
|  | ||||
| @ -14,6 +14,7 @@ extern const Icon I_125_10px; | ||||
| extern const Icon I_ButtonRightSmall_3x5; | ||||
| extern const Icon I_ButtonLeft_4x7; | ||||
| extern const Icon I_ButtonLeftSmall_3x5; | ||||
| extern const Icon I_DFU_128x50; | ||||
| extern const Icon I_Warning_30x23; | ||||
| extern const Icon I_ButtonRight_4x7; | ||||
| extern const Icon I_ButtonCenter_7x7; | ||||
| @ -28,6 +29,10 @@ extern const Icon I_DolphinFirstStart8_56x51; | ||||
| extern const Icon I_DolphinFirstStart7_61x51; | ||||
| extern const Icon I_Flipper_young_80x60; | ||||
| extern const Icon I_DolphinFirstStart3_57x48; | ||||
| extern const Icon I_Scanning_123x52; | ||||
| extern const Icon I_Quest_7x8; | ||||
| extern const Icon I_Unlock_7x8; | ||||
| extern const Icon I_Lock_7x8; | ||||
| extern const Icon I_PassportBottom_128x17; | ||||
| extern const Icon I_DoorLeft_8x56; | ||||
| extern const Icon I_DoorLocked_10x56; | ||||
| @ -75,8 +80,8 @@ extern const Icon A_Sub1ghz_14; | ||||
| extern const Icon A_Tamagotchi_14; | ||||
| extern const Icon A_U2F_14; | ||||
| extern const Icon A_iButton_14; | ||||
| extern const Icon I_Detailed_chip_17x13; | ||||
| extern const Icon I_Medium_chip_22x21; | ||||
| extern const Icon I_EMV_Chip_14x11; | ||||
| extern const Icon I_Health_16x16; | ||||
| extern const Icon I_FaceCharging_29x14; | ||||
| extern const Icon I_BatteryBody_52x28; | ||||
|  | ||||
							
								
								
									
										
											BIN
										
									
								
								assets/icons/Common/DFU_128x50.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 3.1 KiB | 
							
								
								
									
										
											BIN
										
									
								
								assets/icons/GubGHz/Scanning_123x52.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 1.7 KiB | 
| Before Width: | Height: | Size: 3.6 KiB After Width: | Height: | Size: 3.5 KiB | 
							
								
								
									
										
											BIN
										
									
								
								assets/icons/GubGHz/quest_7x8.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 3.6 KiB | 
							
								
								
									
										
											BIN
										
									
								
								assets/icons/GubGHz/unlock_7x8.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 3.5 KiB | 
							
								
								
									
										
											BIN
										
									
								
								assets/icons/NFC/Detailed_chip_17x13.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 981 B | 
							
								
								
									
										148
									
								
								assets/resources/nfc/emv/aid.nfc
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @ -0,0 +1,148 @@ | ||||
| A00000000305076010 VISA ELO Credit | ||||
| A0000000031010 VISA Debit/Credit (Classic) | ||||
| A000000003101001 VISA Credit | ||||
| A000000003101002 VISA Debit | ||||
| A0000000032010 VISA Electron | ||||
| A0000000032020 VISA | ||||
| A0000000033010 VISA Interlink | ||||
| A0000000034010 VISA Specific | ||||
| A0000000035010 VISA Specific | ||||
| A0000000036010 Domestic Visa Cash | ||||
| A0000000036020 International Visa Cash | ||||
| A0000000038002 VISA Auth EMV-CAP (DPA) | ||||
| A0000000038010 VISA Plus | ||||
| A0000000039010 VISA Loyalty | ||||
| A000000003999910 VISA Proprietary ATM | ||||
| A00000000401 MasterCard PayPass | ||||
| A0000000041010 MasterCard Global | ||||
| A00000000410101213 MasterCard Credit | ||||
| A00000000410101215 MasterCard Credit | ||||
| A0000000042010 MasterCard Specific | ||||
| A0000000043010 MasterCard Specific | ||||
| A0000000043060 Maestro (Debit) | ||||
| A000000004306001 Maestro (Debit) | ||||
| A0000000044010 MasterCard Specific | ||||
| A0000000045010 MasterCard Specific | ||||
| A0000000046000 Cirrus | ||||
| A0000000048002 SecureCode EMV-CAP | ||||
| A0000000049999 MasterCard PayPass | ||||
| A0000000050001 Maestro UK | ||||
| A0000000050002 Solo | ||||
| A00000002401 Self Service | ||||
| A000000025 American Express | ||||
| A0000000250000 American Express | ||||
| A00000002501 American Express | ||||
| A000000025010402 American Express | ||||
| A000000025010701 ExpressPay | ||||
| A000000025010801 American Express | ||||
| A0000000291010 Link / American Express | ||||
| A0000000421010 Cartes Bancaire EMV Card | ||||
| A0000000426010 Apple Pay | ||||
| A00000006510 JCB | ||||
| A0000000651010 JCB J Smart Credit | ||||
| A00000006900 Moneo | ||||
| A000000077010000021000000000003B Visa AEPN | ||||
| A000000098 Debit Card | ||||
| A0000000980848 Debit Card | ||||
| A0000001211010 Dankort VISA GEM Vision | ||||
| A0000001410001 PagoBANCOMAT | ||||
| A0000001523010 Discover, Pulse D Pas | ||||
| A0000001524010 Discover | ||||
| A0000001544442 Banricompras Debito | ||||
| A000000172950001 BAROC Taiwan | ||||
| A0000002281010 SPAN (M/Chip) | ||||
| A0000002282010 SPAN (VIS) | ||||
| A0000002771010 INTERAC | ||||
| A00000031510100528 Currence PuC | ||||
| A0000003156020 Chipknip | ||||
| A0000003591010028001 Girocard EAPS | ||||
| A0000003710001 InterSwitch Verve Card | ||||
| A0000004540010 Etranzact Genesis Card | ||||
| A0000004540011 Etranzact Genesis Card 2 | ||||
| A0000004766C GOOGLE_PAYMENT | ||||
| A0000005241010 RuPay | ||||
| A0000006723010 TROY chip credit card | ||||
| A0000006723020 TROY chip debit card | ||||
| A0000007705850 XTRAPOWER | ||||
| B012345678 Maestro TEST | ||||
| D27600002545500100 Girocard | ||||
| D5780000021010 Bankaxept | ||||
| F0000000030001 BRADESCO | ||||
| A000000003000000 (VISA) Card Manager | ||||
| A000000003534441 Schlumberger SD | ||||
| A0000000035350 Security Domain | ||||
| A000000003535041 Security Domain | ||||
| A0000000040000 MasterCard Card Manager | ||||
| A000000018434D Gemplus card manager | ||||
| A000000018434D00 Gemplus Security Domain | ||||
| A0000000960200 Proton WISD | ||||
| A0000001510000 Global Platform SD | ||||
| A00000015153504341534400 CASD_AID | ||||
| A000000476A010 GSD_MANAGER_AID | ||||
| A000000476A110 GSD_MANAGER_AID | ||||
| 315041592E5359532E4444463031 Visa PSE  | ||||
| 325041592E5359532E4444463031 Visa PPSE | ||||
| A0000000042203 MasterCard Specific | ||||
| A0000000045555 APDULogger | ||||
| A0000000090001FF44FF1289 Orange | ||||
| A0000000101030 Maestro-CH | ||||
| A00000001800 Gemplus | ||||
| A0000000181001 gemplus util packages | ||||
| A000000025010104 American Express | ||||
| A00000002949034010100001 HSBC | ||||
| A00000002949282010100000 Barclay | ||||
| A00000005945430100 Girocard Electronic Cash | ||||
| A0000000980840 Visa Common Debit | ||||
| A0000001570010 AMEX | ||||
| A0000001570020 MasterCard | ||||
| A0000001570021 Maestro | ||||
| A0000001570022 Maestro | ||||
| A0000001570023 CASH | ||||
| A0000001570030 VISA | ||||
| A0000001570031 VISA | ||||
| A0000001570040 JCB | ||||
| A0000001570050 Postcard | ||||
| A0000001570051 Postcard | ||||
| A0000001570100 MCard | ||||
| A0000001570104 MyOne | ||||
| A000000157010C WIRCard | ||||
| A000000157010D Power Card | ||||
| A0000001574443 DINERS CLUB | ||||
| A0000001574444 Supercard Plus | ||||
| A00000022820101010 SPAN | ||||
| A000000308000010000100 ID-ONE PIV BIO | ||||
| A0000003241010 Discover Zip | ||||
| A000000333010101 UnionPay Debit | ||||
| A000000333010102 UnionPay Credit | ||||
| A000000333010103 UnionPay Quasi Credit | ||||
| A000000333010106 UnionPay Electronic Cash | ||||
| A000000333010108 U.S. UnionPay Common Debit | ||||
| A000000337102000 Classic | ||||
| A000000337101001 Prepaye Online | ||||
| A000000337102001 Prepaye Possibile Offiline | ||||
| A000000337601001 Porte Monnaie Electronique | ||||
| A0000006581010 MIR Credit | ||||
| A0000006581011 MIR Credit | ||||
| A0000006582010 MIR Debit | ||||
| D040000001000002 Paylife Quick IEP | ||||
| D040000002000002 RFU | ||||
| D040000003000002 POS | ||||
| D040000004000002 ATM | ||||
| D04000000B000002 Retail | ||||
| D04000000C000002 Bank_Data | ||||
| D04000000D000002 Shopping | ||||
| D040000013000001 DF_UNI_Kepler1 | ||||
| D040000013000001 DF_Schüler1 | ||||
| D040000013000002 DF_UNI_Kepler2 | ||||
| D040000013000002 DF_Schüler2 | ||||
| D040000014000001 DF_Mensa | ||||
| D040000015000001 DF_UNI_Ausweis | ||||
| D040000015000001 DF_Ausweis | ||||
| D0400000190001 EMV ATM Maestro | ||||
| D0400000190002 EMV POS Maestro | ||||
| D0400000190003 EMV ATM MasterCard | ||||
| D0400000190004 EMV POS MasterCard | ||||
| D276000025 Girocard | ||||
| D27600002547410100 Girocard ATM | ||||
| D7560000010101 Reka Card | ||||
| D7560000300101 M Budget | ||||
							
								
								
									
										249
									
								
								assets/resources/nfc/emv/country_code.nfc
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @ -0,0 +1,249 @@ | ||||
| 0004 AFG | ||||
| 0008 ALB | ||||
| 0010 ATA | ||||
| 0012 DZA | ||||
| 0016 ASM | ||||
| 0020 AND | ||||
| 0024 AGO | ||||
| 0028 ATG | ||||
| 0031 AZE | ||||
| 0032 ARG | ||||
| 0036 AUS | ||||
| 0040 AUT | ||||
| 0044 BHS | ||||
| 0048 BHR | ||||
| 0050 BGD | ||||
| 0051 ARM | ||||
| 0052 BRB | ||||
| 0056 BEL | ||||
| 0060 BMU | ||||
| 0064 BTN | ||||
| 0068 BOL | ||||
| 0070 BIH | ||||
| 0072 BWA | ||||
| 0074 BVT | ||||
| 0076 BRA | ||||
| 0084 BLZ | ||||
| 0086 IOT | ||||
| 0090 SLB | ||||
| 0092 VGB | ||||
| 0096 BRN | ||||
| 0100 BGR | ||||
| 0104 MMR | ||||
| 0108 BDI | ||||
| 0112 BLR | ||||
| 0116 KHM | ||||
| 0120 CMR | ||||
| 0124 CAN | ||||
| 0132 CPV | ||||
| 0136 CYM | ||||
| 0140 CAF | ||||
| 0144 LKA | ||||
| 0148 TCD | ||||
| 0152 CHL | ||||
| 0156 CHN | ||||
| 0158 TWN | ||||
| 0162 CXR | ||||
| 0166 CCK | ||||
| 0170 COL | ||||
| 0174 COM | ||||
| 0175 MYT | ||||
| 0178 COG | ||||
| 0180 COD | ||||
| 0184 COK | ||||
| 0188 CRI | ||||
| 0191 HRV | ||||
| 0192 CUB | ||||
| 0196 CYP | ||||
| 0203 CZE | ||||
| 0204 BEN | ||||
| 0208 DNK | ||||
| 0212 DMA | ||||
| 0214 DOM | ||||
| 0218 ECU | ||||
| 0222 SLV | ||||
| 0226 GNQ | ||||
| 0231 ETH | ||||
| 0232 ERI | ||||
| 0233 EST | ||||
| 0234 FRO | ||||
| 0238 FLK | ||||
| 0239 SGS | ||||
| 0242 FJI | ||||
| 0246 FIN | ||||
| 0248 ALA | ||||
| 0250 FRA | ||||
| 0254 GUF | ||||
| 0258 PYF | ||||
| 0260 ATF | ||||
| 0262 DJI | ||||
| 0266 GAB | ||||
| 0268 GEO | ||||
| 0270 GMB | ||||
| 0275 PSE | ||||
| 0276 DEU | ||||
| 0288 GHA | ||||
| 0292 GIB | ||||
| 0296 KIR | ||||
| 0300 GRC | ||||
| 0304 GRL | ||||
| 0308 GRD | ||||
| 0312 GLP | ||||
| 0316 GUM | ||||
| 0320 GTM | ||||
| 0324 GIN | ||||
| 0328 GUY | ||||
| 0332 HTI | ||||
| 0334 HMD | ||||
| 0336 VAT | ||||
| 0340 HND | ||||
| 0344 HKG | ||||
| 0348 HUN | ||||
| 0352 ISL | ||||
| 0356 IND | ||||
| 0360 IDN | ||||
| 0364 IRN | ||||
| 0368 IRQ | ||||
| 0372 IRL | ||||
| 0376 ISR | ||||
| 0380 ITA | ||||
| 0384 CIV | ||||
| 0388 JAM | ||||
| 0392 JPN | ||||
| 0398 KAZ | ||||
| 0400 JOR | ||||
| 0404 KEN | ||||
| 0408 PRK | ||||
| 0410 KOR | ||||
| 0414 KWT | ||||
| 0417 KGZ | ||||
| 0418 LAO | ||||
| 0422 LBN | ||||
| 0426 LSO | ||||
| 0428 LVA | ||||
| 0430 LBR | ||||
| 0434 LBY | ||||
| 0438 LIE | ||||
| 0440 LTU | ||||
| 0442 LUX | ||||
| 0446 MAC | ||||
| 0450 MDG | ||||
| 0454 MWI | ||||
| 0458 MYS | ||||
| 0462 MDV | ||||
| 0466 MLI | ||||
| 0470 MLT | ||||
| 0474 MTQ | ||||
| 0478 MRT | ||||
| 0480 MUS | ||||
| 0484 MEX | ||||
| 0492 MCO | ||||
| 0496 MNG | ||||
| 0498 MDA | ||||
| 0499 MNE | ||||
| 0500 MSR | ||||
| 0504 MAR | ||||
| 0508 MOZ | ||||
| 0512 OMN | ||||
| 0516 NAM | ||||
| 0520 NRU | ||||
| 0524 NPL | ||||
| 0528 NLD | ||||
| 0531 CUW | ||||
| 0533 ABW | ||||
| 0534 SXM | ||||
| 0535 BES | ||||
| 0540 NCL | ||||
| 0548 VUT | ||||
| 0554 NZL | ||||
| 0558 NIC | ||||
| 0562 NER | ||||
| 0566 NGA | ||||
| 0570 NIU | ||||
| 0574 NFK | ||||
| 0578 NOR | ||||
| 0580 MNP | ||||
| 0581 UMI | ||||
| 0583 FSM | ||||
| 0584 MHL | ||||
| 0585 PLW | ||||
| 0586 PAK | ||||
| 0591 PAN | ||||
| 0598 PNG | ||||
| 0600 PRY | ||||
| 0604 PER | ||||
| 0608 PHL | ||||
| 0612 PCN | ||||
| 0616 POL | ||||
| 0620 PRT | ||||
| 0624 GNB | ||||
| 0626 TLS | ||||
| 0630 PRI | ||||
| 0634 QAT | ||||
| 0638 REU | ||||
| 0642 ROU | ||||
| 0643 RUS | ||||
| 0646 RWA | ||||
| 0652 BLM | ||||
| 0654 SHN | ||||
| 0659 KNA | ||||
| 0660 AIA | ||||
| 0662 LCA | ||||
| 0663 MAF | ||||
| 0666 SPM | ||||
| 0670 VCT | ||||
| 0674 SMR | ||||
| 0678 STP | ||||
| 0682 SAU | ||||
| 0686 SEN | ||||
| 0688 SRB | ||||
| 0690 SYC | ||||
| 0694 SLE | ||||
| 0702 SGP | ||||
| 0703 SVK | ||||
| 0704 VNM | ||||
| 0705 SVN | ||||
| 0706 SOM | ||||
| 0710 ZAF | ||||
| 0716 ZWE | ||||
| 0724 ESP | ||||
| 0728 SSD | ||||
| 0729 SDN | ||||
| 0732 ESH | ||||
| 0740 SUR | ||||
| 0744 SJM | ||||
| 0748 SWZ | ||||
| 0752 SWE | ||||
| 0756 CHE | ||||
| 0760 SYR | ||||
| 0762 TJK | ||||
| 0764 THA | ||||
| 0768 TGO | ||||
| 0772 TKL | ||||
| 0776 TON | ||||
| 0780 TTO | ||||
| 0784 ARE | ||||
| 0788 TUN | ||||
| 0792 TUR | ||||
| 0795 TKM | ||||
| 0796 TCA | ||||
| 0798 TUV | ||||
| 0800 UGA | ||||
| 0804 UKR | ||||
| 0807 MKD | ||||
| 0818 EGY | ||||
| 0826 GBR | ||||
| 0831 GGY | ||||
| 0832 JEY | ||||
| 0833 IMN | ||||
| 0834 TZA | ||||
| 0840 USA | ||||
| 0850 VIR | ||||
| 0854 BFA | ||||
| 0858 URY | ||||
| 0860 UZB | ||||
| 0862 VEN | ||||
| 0876 WLF | ||||
| 0882 WSM | ||||
| 0887 YEM | ||||
| 0894 ZMB | ||||
							
								
								
									
										168
									
								
								assets/resources/nfc/emv/currency_code.nfc
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @ -0,0 +1,168 @@ | ||||
| 0997 USN | ||||
| 0994 XSU | ||||
| 0990 CLF | ||||
| 0986 BRL | ||||
| 0985 PLN | ||||
| 0984 BOV | ||||
| 0981 GEL | ||||
| 0980 UAH | ||||
| 0979 MXV | ||||
| 0978 EUR | ||||
| 0977 BAM | ||||
| 0976 CDF | ||||
| 0975 BGN | ||||
| 0973 AOA | ||||
| 0972 TJS | ||||
| 0971 AFN | ||||
| 0970 COU | ||||
| 0969 MGA | ||||
| 0968 SRD | ||||
| 0967 ZMW | ||||
| 0965 XUA | ||||
| 0960 XDR | ||||
| 0953 XPF | ||||
| 0952 XOF | ||||
| 0951 XCD | ||||
| 0950 XAF | ||||
| 0949 TRY | ||||
| 0948 CHW | ||||
| 0947 CHE | ||||
| 0946 RON | ||||
| 0944 AZN | ||||
| 0943 MZN | ||||
| 0941 RSD | ||||
| 0940 UYI | ||||
| 0938 SDG | ||||
| 0937 VEF | ||||
| 0936 GHS | ||||
| 0934 TMT | ||||
| 0933 BYN | ||||
| 0932 ZWL | ||||
| 0931 CUC | ||||
| 0930 STN | ||||
| 0929 MRU | ||||
| 0901 TWD | ||||
| 0886 YER | ||||
| 0882 WST | ||||
| 0860 UZS | ||||
| 0858 UYU | ||||
| 0840 USD | ||||
| 0834 TZS | ||||
| 0826 GBP | ||||
| 0818 EGP | ||||
| 0807 MKD | ||||
| 0800 UGX | ||||
| 0788 TND | ||||
| 0784 AED | ||||
| 0780 TTD | ||||
| 0776 TOP | ||||
| 0764 THB | ||||
| 0760 SYP | ||||
| 0756 CHF | ||||
| 0752 SEK | ||||
| 0748 SZL | ||||
| 0728 SSP | ||||
| 0710 ZAR | ||||
| 0706 SOS | ||||
| 0704 VND | ||||
| 0702 SGD | ||||
| 0694 SLL | ||||
| 0690 SCR | ||||
| 0682 SAR | ||||
| 0654 SHP | ||||
| 0646 RWF | ||||
| 0643 RUB | ||||
| 0634 QAR | ||||
| 0608 PHP | ||||
| 0604 PEN | ||||
| 0600 PYG | ||||
| 0598 PGK | ||||
| 0590 PAB | ||||
| 0586 PKR | ||||
| 0578 NOK | ||||
| 0566 NGN | ||||
| 0558 NIO | ||||
| 0554 NZD | ||||
| 0548 VUV | ||||
| 0533 AWG | ||||
| 0532 ANG | ||||
| 0524 NPR | ||||
| 0516 NAD | ||||
| 0512 OMR | ||||
| 0504 MAD | ||||
| 0498 MDL | ||||
| 0496 MNT | ||||
| 0484 MXN | ||||
| 0480 MUR | ||||
| 0462 MVR | ||||
| 0458 MYR | ||||
| 0454 MWK | ||||
| 0446 MOP | ||||
| 0434 LYD | ||||
| 0430 LRD | ||||
| 0426 LSL | ||||
| 0422 LBP | ||||
| 0418 LAK | ||||
| 0417 KGS | ||||
| 0414 KWD | ||||
| 0410 KRW | ||||
| 0408 KPW | ||||
| 0404 KES | ||||
| 0400 JOD | ||||
| 0398 KZT | ||||
| 0392 JPY | ||||
| 0388 JMD | ||||
| 0376 ILS | ||||
| 0368 IQD | ||||
| 0364 IRR | ||||
| 0360 IDR | ||||
| 0356 INR | ||||
| 0352 ISK | ||||
| 0348 HUF | ||||
| 0344 HKD | ||||
| 0340 HNL | ||||
| 0332 HTG | ||||
| 0328 GYD | ||||
| 0324 GNF | ||||
| 0320 GTQ | ||||
| 0292 GIP | ||||
| 0270 GMD | ||||
| 0262 DJF | ||||
| 0242 FJD | ||||
| 0238 FKP | ||||
| 0232 ERN | ||||
| 0230 ETB | ||||
| 0222 SVC | ||||
| 0214 DOP | ||||
| 0208 DKK | ||||
| 0203 CZK | ||||
| 0192 CUP | ||||
| 0191 HRK | ||||
| 0188 CRC | ||||
| 0174 KMF | ||||
| 0170 COP | ||||
| 0156 CNY | ||||
| 0152 CLP | ||||
| 0144 LKR | ||||
| 0136 KYD | ||||
| 0132 CVE | ||||
| 0124 CAD | ||||
| 0116 KHR | ||||
| 0108 BIF | ||||
| 0104 MMK | ||||
| 0096 BND | ||||
| 0090 SBD | ||||
| 0084 BZD | ||||
| 0072 BWP | ||||
| 0068 BOB | ||||
| 0064 BTN | ||||
| 0060 BMD | ||||
| 0052 BBD | ||||
| 0051 AMD | ||||
| 0050 BDT | ||||
| 0048 BHD | ||||
| 0044 BSD | ||||
| 0036 AUD | ||||
| 0032 ARS | ||||
| 0012 DZD | ||||
| 0008 ALL | ||||
| @ -13,13 +13,57 @@ | ||||
| #include <furi-hal.h> | ||||
| #include <u8g2.h> | ||||
| 
 | ||||
| const uint8_t I_Warning_30x23_0[] = { | ||||
|     0x00, 0xC0, 0x00, 0x00, 0x00, 0xE0, 0x01, 0x00, 0x00, 0xF0, 0x03, 0x00, 0x00, 0xF0, 0x03, 0x00, | ||||
|     0x00, 0xF8, 0x07, 0x00, 0x00, 0x3C, 0x0F, 0x00, 0x00, 0x3C, 0x0F, 0x00, 0x00, 0x3E, 0x1F, 0x00, | ||||
|     0x00, 0x3F, 0x3F, 0x00, 0x00, 0x3F, 0x3F, 0x00, 0x80, 0x3F, 0x7F, 0x00, 0xC0, 0x3F, 0xFF, 0x00, | ||||
|     0xC0, 0x3F, 0xFF, 0x00, 0xE0, 0x3F, 0xFF, 0x01, 0xF0, 0x3F, 0xFF, 0x03, 0xF0, 0x3F, 0xFF, 0x03, | ||||
|     0xF8, 0x3F, 0xFF, 0x07, 0xFC, 0xFF, 0xFF, 0x0F, 0xFC, 0xFF, 0xFF, 0x0F, 0xFE, 0x3F, 0xFF, 0x1F, | ||||
|     0xFF, 0x3F, 0xFF, 0x3F, 0xFF, 0xFF, 0xFF, 0x3F, 0xFE, 0xFF, 0xFF, 0x1F, | ||||
| const uint8_t I_DFU_128x50[] = { | ||||
|     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x07, 0x00, 0x00, 0x00, | ||||
|     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0x00, 0x38, 0x00, 0x00, 0x00, | ||||
|     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0E, 0x00, 0xC0, 0x00, 0x00, 0x00, | ||||
|     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x8F, 0x01, 0x00, 0x00, 0x03, 0x00, 0x00, | ||||
|     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x7F, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, | ||||
|     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0xFF, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, | ||||
|     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0xFF, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, | ||||
|     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0xFF, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, | ||||
|     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0x75, 0x00, 0x00, 0xF0, 0x3F, 0x00, 0x00, | ||||
|     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xFF, 0x0A, 0x00, 0x00, 0x0F, 0x60, 0x00, 0x00, | ||||
|     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0xE0, 0x0F, 0x00, 0xC0, 0xE0, 0x4F, 0x00, 0x00, | ||||
|     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x18, 0x00, 0x30, 0x1E, 0x90, 0x00, 0x00, | ||||
|     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x18, 0x00, 0x8C, 0x01, 0xA0, 0x00, 0x00, | ||||
|     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x81, 0xFF, 0x19, 0x00, 0x63, 0x00, 0xC0, 0xF0, 0x07, | ||||
|     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x60, 0x5E, 0x1F, 0x80, 0x18, 0x00, 0xE0, 0x0E, 0x18, | ||||
|     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x18, 0xAF, 0x0F, 0x40, 0x06, 0x00, 0xF8, 0x01, 0x20, | ||||
|     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x06, 0x57, 0x01, 0x20, 0x01, 0x00, 0x78, 0x00, 0x3E, | ||||
|     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x84, 0x81, 0xAF, 0x02, 0x90, 0x00, 0x00, 0x38, 0x80, 0x41, | ||||
|     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x63, 0x80, 0x57, 0x01, 0x48, 0x00, 0x00, 0x10, 0x60, 0x40, | ||||
|     0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0x10, 0x80, 0xAB, 0x00, 0x24, 0x00, 0x00, 0x08, 0x10, 0x40, | ||||
|     0x3F, 0x00, 0x00, 0x00, 0x00, 0x38, 0x0C, 0xC0, 0x57, 0x01, 0x12, 0x00, 0x00, 0x04, 0x08, 0x40, | ||||
|     0xC0, 0x0F, 0x00, 0x00, 0xC0, 0x07, 0x03, 0xF0, 0xAB, 0x00, 0x0A, 0x00, 0x00, 0x02, 0x04, 0x40, | ||||
|     0x00, 0xF0, 0x1F, 0x80, 0x3F, 0xC0, 0x00, 0xFC, 0x55, 0x01, 0x05, 0xE0, 0x00, 0x01, 0x04, 0x40, | ||||
|     0x00, 0x00, 0xE0, 0x7F, 0x00, 0x30, 0x00, 0xFF, 0xAB, 0x00, 0x05, 0xE0, 0x80, 0x00, 0x02, 0x40, | ||||
|     0x0F, 0x00, 0x00, 0x00, 0x80, 0x0F, 0xE0, 0xCF, 0x55, 0x81, 0x02, 0xF0, 0x40, 0x00, 0x02, 0x40, | ||||
|     0xF0, 0x0F, 0x00, 0x00, 0x7F, 0x00, 0xFE, 0xC3, 0xAB, 0x80, 0x02, 0x78, 0x20, 0x00, 0x01, 0x40, | ||||
|     0x00, 0xF0, 0xFF, 0xFF, 0x00, 0xF0, 0xFF, 0xC0, 0xD5, 0x81, 0x01, 0x7E, 0x10, 0x80, 0x00, 0x20, | ||||
|     0x00, 0x00, 0x00, 0x00, 0xC0, 0xFF, 0x0F, 0xE0, 0xFA, 0x83, 0xC1, 0x3F, 0x08, 0x80, 0x00, 0x20, | ||||
|     0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x01, 0xD8, 0x07, 0x83, 0xF1, 0x1F, 0x04, 0x40, 0x00, 0x20, | ||||
|     0x00, 0xE0, 0xFF, 0xFF, 0xFF, 0x0F, 0x80, 0xC7, 0x01, 0x83, 0xF1, 0x0F, 0x00, 0x20, 0x00, 0x10, | ||||
|     0xE0, 0xFF, 0xFF, 0xFF, 0x3F, 0xC0, 0x7F, 0x40, 0x80, 0x83, 0xE1, 0x01, 0x00, 0x20, 0x00, 0x18, | ||||
|     0xFC, 0xFF, 0xFF, 0xFF, 0x03, 0x3F, 0x00, 0x20, 0xFC, 0x83, 0x01, 0x00, 0x00, 0x10, 0x00, 0x18, | ||||
|     0xFF, 0xFF, 0xFF, 0x3F, 0xF0, 0x00, 0x00, 0x10, 0xD7, 0x01, 0x03, 0x00, 0x00, 0x08, 0x00, 0x1C, | ||||
|     0xFF, 0xFF, 0x01, 0x00, 0x0F, 0x00, 0x00, 0x88, 0xAB, 0x02, 0xE3, 0x01, 0x00, 0x08, 0x00, 0x0C, | ||||
|     0xFF, 0x07, 0x00, 0xE0, 0x00, 0x00, 0x00, 0xC4, 0x55, 0x05, 0x1E, 0x00, 0x00, 0x04, 0x00, 0x0E, | ||||
|     0x7F, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x00, 0xA3, 0xAB, 0x02, 0x06, 0x00, 0x00, 0x02, 0x00, 0x0F, | ||||
|     0x0F, 0x00, 0x80, 0x03, 0x00, 0x00, 0xC0, 0x10, 0x57, 0x05, 0x02, 0x00, 0x00, 0x01, 0x80, 0x07, | ||||
|     0x03, 0x00, 0x70, 0x00, 0x00, 0x00, 0x30, 0x08, 0xAB, 0x0A, 0x02, 0x00, 0xC0, 0x00, 0xC0, 0x07, | ||||
|     0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x0C, 0x84, 0x57, 0x15, 0x01, 0x00, 0x30, 0x00, 0xE0, 0x07, | ||||
|     0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0xC3, 0xFF, 0x2A, 0x01, 0x00, 0x0C, 0x00, 0xF0, 0x0F, | ||||
|     0x00, 0xC0, 0x00, 0x00, 0x00, 0xE0, 0xC0, 0xE0, 0xFE, 0x55, 0x01, 0x82, 0x03, 0x00, 0xF8, 0x15, | ||||
|     0x00, 0x30, 0x00, 0x00, 0x00, 0x1C, 0x30, 0x78, 0xFE, 0xAA, 0x01, 0x7C, 0x00, 0x00, 0xFC, 0x23, | ||||
|     0x00, 0x0E, 0x00, 0x00, 0xC0, 0x03, 0x0C, 0x3C, 0x7F, 0x5D, 0x01, 0x00, 0x00, 0x00, 0xFF, 0x45, | ||||
|     0xC0, 0x01, 0x00, 0x00, 0x3E, 0x00, 0x02, 0x8F, 0xBF, 0xAE, 0x03, 0x00, 0x00, 0xC0, 0xFF, 0x82, | ||||
|     0x30, 0x00, 0x00, 0xC0, 0x01, 0x80, 0xC1, 0x43, 0xFE, 0x5D, 0x01, 0x00, 0x00, 0xF0, 0xFF, 0x05, | ||||
|     0x0F, 0x00, 0x80, 0x3F, 0x00, 0x60, 0xF0, 0x31, 0xF6, 0xAE, 0x03, 0x00, 0x00, 0xFA, 0xAF, 0x02, | ||||
|     0xFC, 0xFF, 0x7F, 0x00, 0x00, 0x18, 0x7C, 0x08, 0x23, 0xFF, 0x05, 0x00, 0x00, 0xFD, 0x55, 0x01, | ||||
|     0x00, 0x00, 0x00, 0x00, 0x00, 0x86, 0x1F, 0x84, 0x30, 0xFE, 0x0A, 0x00, 0x00, 0xAA, 0xAA, 0x00, | ||||
|     0x00, 0x00, 0x00, 0x00, 0x80, 0xF1, 0x07, 0x43, 0x18, 0xFF, 0x15, 0x00, 0x00, 0x54, 0x15, 0x00, | ||||
|     0x00, 0x00, 0x00, 0x00, 0xF8, 0xFF, 0x80, 0x20, 0x8C, 0xFF, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, | ||||
| }; | ||||
| 
 | ||||
| // Boot request enum
 | ||||
| @ -138,8 +182,6 @@ void target_version_save(void) { | ||||
| 
 | ||||
| void target_usb_wire_reset() { | ||||
|     LL_GPIO_ResetOutputPin(BOOT_USB_PORT, BOOT_USB_PIN); | ||||
|     LL_mDelay(10); | ||||
|     LL_GPIO_SetOutputPin(BOOT_USB_PORT, BOOT_USB_PIN); | ||||
| } | ||||
| 
 | ||||
| void target_display_init() { | ||||
| @ -155,10 +197,9 @@ void target_display_init() { | ||||
|     u8g2_ClearBuffer(&fb); | ||||
|     u8g2_SetDrawColor(&fb, 0x01); | ||||
|     u8g2_SetFont(&fb, u8g2_font_helvB08_tf); | ||||
|     u8g2_DrawStr(&fb, 2, 8, "Recovery & Update Mode"); | ||||
|     u8g2_DrawXBM(&fb, 49, 14, 30, 23, I_Warning_30x23_0); | ||||
|     u8g2_DrawStr(&fb, 2, 50, "DFU Bootloader activated"); | ||||
|     u8g2_DrawStr(&fb, 6, 62, "www.flipp.dev/recovery"); | ||||
|     u8g2_DrawXBM(&fb, 0, 64 - 50, 128, 50, I_DFU_128x50); | ||||
|     u8g2_DrawStr(&fb, 2, 8, "Update & Recovery Mode"); | ||||
|     u8g2_DrawStr(&fb, 2, 21, "DFU started"); | ||||
|     // Send buffer
 | ||||
|     u8g2_SetPowerSave(&fb, 0); | ||||
|     u8g2_SendBuffer(&fb); | ||||
|  | ||||
| @ -1,144 +0,0 @@ | ||||
| #include <furi.h> | ||||
| 
 | ||||
| #ifdef __cplusplus | ||||
| extern "C" { | ||||
| #endif | ||||
| 
 | ||||
| /*
 | ||||
| struct used for handling SPI info. | ||||
| */ | ||||
| typedef struct { | ||||
|     SPI_HandleTypeDef* spi; | ||||
|     PubSubCallback cb; | ||||
|     void* ctx; | ||||
| } SpiHandle; | ||||
| 
 | ||||
| /*
 | ||||
| For transmit/receive data use `spi_xfer` function. | ||||
| 
 | ||||
| * `tx_data` and `rx_data` size must be equal (and equal `len`) | ||||
| * `cb` called after spi operation is completed, `(NULL, ctx)` passed to callback. | ||||
| */ | ||||
| bool spi_xfer( | ||||
|     SPI_HandleTypeDef* spi, | ||||
|     uint8_t* tx_data, | ||||
|     uint8_t* rx_data, | ||||
|     size_t len, | ||||
|     PubSubCallback cb, | ||||
|     void* ctx); | ||||
| 
 | ||||
| /*
 | ||||
| Blocking verison: | ||||
| */ | ||||
| static inline bool | ||||
|     spi_xfer_block(SPI_HandleTypeDef* spi, uint8_t* tx_data, uint8_t* rx_data, size_t len) { | ||||
|     semaphoreInfo s; | ||||
|     osSemaphore block = createSemaphoreStatic(s); | ||||
|     if(!spi_xfer(spi, tx_data, rx_data, len, RELEASE_SEMAPHORE, (void*)block)) { | ||||
|         osReleaseSemaphore(block); | ||||
|         return false; | ||||
|     } | ||||
|     osWaitSemaphore(block); | ||||
|     return false; | ||||
| } | ||||
| 
 | ||||
| /*
 | ||||
| Common implementation of SPI bus: serial interface + CS pin | ||||
| */ | ||||
| typedef struct { | ||||
|     GpioPin* cs; ///< CS pin
 | ||||
|     ValueMutex* spi; ///< <SpiHandle*>
 | ||||
| } SpiBus; | ||||
| 
 | ||||
| /*
 | ||||
| For dedicated work with one device there is `SpiDevice` entity. | ||||
| It contains ValueMutex around SpiBus: after you acquire device | ||||
| you can acquire spi to work with it (don't forget SPI bus is shared | ||||
| around many device, release it after every transaction as quick as possible). | ||||
| */ | ||||
| typedef struct { | ||||
|     ValueMutex* bus; ///< <SpiBus*>
 | ||||
| } SpiDevice; | ||||
| 
 | ||||
| ##SPI IRQ device | ||||
| 
 | ||||
|     /*
 | ||||
| Many devices (like CC1101 and NFC) present as SPI bus and IRQ line. | ||||
| For work with it there is special entity `SpiIrqDevice`. | ||||
| Use `subscribe_pubsub` for subscribinq to irq events. | ||||
| */ | ||||
| 
 | ||||
|     typedef struct { | ||||
|     ValueMutex* bus; ///< <SpiBus*>
 | ||||
|     PubSub* irq; | ||||
| } SpiIrqDevice; | ||||
| 
 | ||||
| /*
 | ||||
| Special implementation of SPI bus: serial interface + CS, Res, D/I lines. | ||||
| */ | ||||
| typedef struct { | ||||
|     GpioPin* cs; ///< CS pin
 | ||||
|     GpioPin* res; ///< reset pin
 | ||||
|     GpioPin* di; ///< D/I pin
 | ||||
|     ValueMutex* spi; ///< <SPI_HandleTypeDef*>
 | ||||
| } DisplayBus; | ||||
| 
 | ||||
| typedef struct { | ||||
|     ValueMutex* bus; ///< <DisplayBus*>
 | ||||
| } DisplayDevice; | ||||
| 
 | ||||
| /*
 | ||||
| # SPI devices (F2) | ||||
| 
 | ||||
| * `/dev/sdcard` - SD card SPI, `SpiDevice` | ||||
| * `/dev/cc1101_bus` - Sub-GHz radio (CC1101), `SpiIrqDevice` | ||||
| * `/dev/nfc` - NFC (ST25R3916), `SpiIrqDevice` | ||||
| * `/dev/display` - `DisplayDevice` | ||||
| * `/dev/spiext` - External SPI (warning! Lock PA4, PA5, PA6, PA7) | ||||
| 
 | ||||
| ### Application example | ||||
| 
 | ||||
| ```C | ||||
| // Be careful, this function called from IRQ context
 | ||||
| void handle_irq(void* _arg, void* _ctx) { | ||||
| } | ||||
| 
 | ||||
| void cc1101_example() { | ||||
|     SpiIrqDevice* cc1101_device = open_input("/dev/cc1101_bus"); | ||||
|     if(cc1101_device == NULL) return; // bus not available, critical error
 | ||||
| 
 | ||||
|     subscribe_pubsub(cc1101_device->irq, handle_irq, NULL); | ||||
| 
 | ||||
|     { | ||||
|         // acquire device as device bus
 | ||||
|         SpiBus* spi_bus = acquire_mutex(cc1101_device->bus, 0); | ||||
|         if(spi_bus == NULL) { | ||||
|             printf("Device busy\n"); | ||||
|             // wait for device
 | ||||
|             spi_bus = acquire_mutex_block(cc1101_device->bus); | ||||
|         } | ||||
|          | ||||
|         // make transaction
 | ||||
|         uint8_t request[4] = {0xDE, 0xAD, 0xBE, 0xEF}; | ||||
|         uint8_t response[4]; | ||||
| 
 | ||||
|         { | ||||
|             SPI_HandleTypeDef* spi = acquire_mutex_block(spi_bus->spi); | ||||
| 
 | ||||
|             gpio_write(spi_bus->cs, false); | ||||
|             spi_xfer_block(spi, request, response, 4); | ||||
|             gpio_write(spi_bus->cs, true); | ||||
| 
 | ||||
|             release_mutex(cc1101_device->spi, spi); | ||||
|         } | ||||
| 
 | ||||
|         // release device (device bus)
 | ||||
|         release_mutex(cc1101_device->bus, spi_bus); | ||||
|     } | ||||
| } | ||||
| ``` | ||||
| */ | ||||
| 
 | ||||
| #ifdef __cplusplus | ||||
| } | ||||
| #endif | ||||
| @ -1,37 +1,30 @@ | ||||
| #include "check.h" | ||||
| #include "furi-hal-task.h" | ||||
| #include <furi-hal-console.h> | ||||
| #include <stdio.h> | ||||
| 
 | ||||
| void __furi_abort(void); | ||||
| 
 | ||||
| // TODO printf doesnt work in ISR context
 | ||||
| void __furi_check(void) { | ||||
|     printf("assertion failed in release mode, switch to debug mode to see full assert info"); | ||||
|     __furi_abort(); | ||||
| } | ||||
| 
 | ||||
| // TODO printf doesnt work in ISR context
 | ||||
| void __furi_check_debug(const char* file, int line, const char* function, const char* condition) { | ||||
|     printf( | ||||
|         "assertion \"%s\" failed: file \"%s\", line %d%s%s", | ||||
|         condition, | ||||
|         file, | ||||
|         line, | ||||
|         function ? ", function: " : "", | ||||
|         function ? function : ""); | ||||
| 
 | ||||
| void __furi_print_name(void) { | ||||
|     furi_hal_console_puts("\r\n\033[0;31m[E]"); | ||||
|     if(task_is_isr_context()) { | ||||
|         printf(" in [ISR] context"); | ||||
|         furi_hal_console_puts("[ISR] "); | ||||
|     } else { | ||||
|         // FuriApp* app = find_task(xTaskGetCurrentTaskHandle());
 | ||||
| 
 | ||||
|         // if(app == NULL) {
 | ||||
|         //     printf(", in [main] context");
 | ||||
|         // } else {
 | ||||
|         //     printf(", in [%s] app context", app->name);
 | ||||
|         // }
 | ||||
|         const char* name = osThreadGetName(osThreadGetId()); | ||||
|         if(name == NULL) { | ||||
|             furi_hal_console_puts("[main] "); | ||||
|         } else { | ||||
|             furi_hal_console_puts("["); | ||||
|             furi_hal_console_puts(name); | ||||
|             furi_hal_console_puts("] "); | ||||
|         } | ||||
|     } | ||||
|     furi_hal_console_puts("\033[0m"); | ||||
| } | ||||
| 
 | ||||
| void __furi_check(void) { | ||||
|     __furi_print_name(); | ||||
|     furi_hal_console_puts("assertion failed\r\n"); | ||||
|     __furi_abort(); | ||||
| } | ||||
| 
 | ||||
|  | ||||
| @ -38,7 +38,6 @@ extern "C" { | ||||
| // !NDEBUG
 | ||||
| 
 | ||||
| void __furi_check(void); | ||||
| void __furi_check_debug(const char* file, int line, const char* function, const char* condition); | ||||
| 
 | ||||
| #ifdef __cplusplus | ||||
| } | ||||
|  | ||||
| @ -31,3 +31,12 @@ | ||||
| #ifndef COUNT_OF | ||||
| #define COUNT_OF(x) (sizeof(x) / sizeof(x[0])) | ||||
| #endif | ||||
| 
 | ||||
| #ifndef FURI_SWAP | ||||
| #define FURI_SWAP(x, y)     \ | ||||
|     do {                    \ | ||||
|         typeof(x) SWAP = x; \ | ||||
|         x = y;              \ | ||||
|         y = SWAP;           \ | ||||
|     } while(0) | ||||
| #endif | ||||
|  | ||||
| @ -1,10 +1,10 @@ | ||||
| FROM ubuntu:focal | ||||
| FROM ubuntu:hirsute | ||||
| 
 | ||||
| RUN apt-get update && DEBIAN_FRONTEND=noninteractive apt-get install --no-install-recommends -y \ | ||||
|         ca-certificates \ | ||||
|         build-essential \ | ||||
|         python3 \ | ||||
|         python-lxml \ | ||||
|         python3-lxml \ | ||||
|         git \ | ||||
|         clang-format-12 \ | ||||
|         dfu-util \ | ||||
| @ -14,10 +14,10 @@ RUN apt-get update && DEBIAN_FRONTEND=noninteractive apt-get install --no-instal | ||||
|         wget && \ | ||||
|     apt-get clean && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* | ||||
| 
 | ||||
| RUN wget --progress=dot:giga "https://developer.arm.com/-/media/Files/downloads/gnu-rm/10-2020q4/gcc-arm-none-eabi-10-2020-q4-major-$(uname -m)-linux.tar.bz2" && \ | ||||
|     tar xjf gcc-arm-none-eabi-10-2020-q4-major-$(uname -m)-linux.tar.bz2 && \ | ||||
|     rm gcc-arm-none-eabi-10-2020-q4-major-$(uname -m)-linux.tar.bz2 && \ | ||||
|     cd gcc-arm-none-eabi-10-2020-q4-major/bin/ && \ | ||||
| RUN wget --progress=dot:giga "https://developer.arm.com/-/media/Files/downloads/gnu-rm/10.3-2021.07/gcc-arm-none-eabi-10.3-2021.07-$(uname -m)-linux.tar.bz2" && \ | ||||
|     tar xjf gcc-arm-none-eabi-10.3-2021.07-$(uname -m)-linux.tar.bz2 && \ | ||||
|     rm gcc-arm-none-eabi-10.3-2021.07-$(uname -m)-linux.tar.bz2 && \ | ||||
|     cd gcc-arm-none-eabi-10.3-2021.07/bin/ && \ | ||||
|     rm -rf ../share && \ | ||||
|     for file in * ; do ln -s "${PWD}/${file}" "/usr/bin/${file}" ; done && \ | ||||
|     cd / && arm-none-eabi-gcc -v && arm-none-eabi-gdb -v | ||||
|  | ||||
 Aleksandr Kutuzov
						Aleksandr Kutuzov