Merge remote-tracking branch 'origin/dev' into release-candidate
							
								
								
									
										27
									
								
								.github/workflows/pvs_studio.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						| @ -43,36 +43,15 @@ jobs: | |||||||
|           fi |           fi | ||||||
|           python3 scripts/get_env.py "--event_file=${{ github.event_path }}" "--type=$TYPE" |           python3 scripts/get_env.py "--event_file=${{ github.event_path }}" "--type=$TYPE" | ||||||
| 
 | 
 | ||||||
|       - name: 'Make reports directory' |       - name: 'Supply PVS credentials' | ||||||
|         run: | |         run: | | ||||||
|           rm -rf reports/ |  | ||||||
|           mkdir reports |  | ||||||
| 
 |  | ||||||
|       - name: 'Generate compile_comands.json' |  | ||||||
|         run: | |  | ||||||
|           ./fbt COMPACT=1 version_json proto_ver icons firmware_cdb dolphin_internal dolphin_blocking _fap_icons api_syms |  | ||||||
| 
 |  | ||||||
|       - name: 'Static code analysis' |  | ||||||
|         run: | |  | ||||||
|           source scripts/toolchain/fbtenv.sh |  | ||||||
|           pvs-studio-analyzer credentials ${{ secrets.PVS_STUDIO_CREDENTIALS }} |           pvs-studio-analyzer credentials ${{ secrets.PVS_STUDIO_CREDENTIALS }} | ||||||
|           pvs-studio-analyzer analyze \ |  | ||||||
|               @.pvsoptions \ |  | ||||||
|               -C gccarm \ |  | ||||||
|               -j$(grep -c processor /proc/cpuinfo) \ |  | ||||||
|               -f build/f7-firmware-DC/compile_commands.json \ |  | ||||||
|               -o PVS-Studio.log |  | ||||||
| 
 | 
 | ||||||
|       - name: 'Convert PVS-Studio output to html and detect warnings' |       - name: 'Convert PVS-Studio output to html and detect warnings' | ||||||
|         id: pvs-warn |         id: pvs-warn | ||||||
|         run: | |         run: | | ||||||
|           WARNINGS=0 |           WARNINGS=0 | ||||||
|           plog-converter \ |           ./fbt COMPACT=1 PVSNOBROWSER=1 firmware_pvs || WARNINGS=1 | ||||||
|               -a GA:1,2,3 \ |  | ||||||
|               -t fullhtml \ |  | ||||||
|               --indicate-warnings \ |  | ||||||
|               PVS-Studio.log \ |  | ||||||
|               -o reports/${DEFAULT_TARGET}-${SUFFIX} || WARNINGS=1 |  | ||||||
|           echo "warnings=${WARNINGS}" >> $GITHUB_OUTPUT |           echo "warnings=${WARNINGS}" >> $GITHUB_OUTPUT | ||||||
| 
 | 
 | ||||||
|       - name: 'Upload artifacts to update server' |       - name: 'Upload artifacts to update server' | ||||||
| @ -84,7 +63,7 @@ jobs: | |||||||
|           chmod 600 ./deploy_key; |           chmod 600 ./deploy_key; | ||||||
|           rsync -avrzP --mkpath \ |           rsync -avrzP --mkpath \ | ||||||
|               -e 'ssh -p ${{ secrets.RSYNC_DEPLOY_PORT }} -i ./deploy_key' \ |               -e 'ssh -p ${{ secrets.RSYNC_DEPLOY_PORT }} -i ./deploy_key' \ | ||||||
|               reports/ ${{ secrets.RSYNC_DEPLOY_USER }}@${{ secrets.RSYNC_DEPLOY_HOST }}:/home/data/firmware-pvs-studio-report/"${BRANCH_NAME}/"; |               build/f7-firmware-DC/pvsreport/ ${{ secrets.RSYNC_DEPLOY_USER }}@${{ secrets.RSYNC_DEPLOY_HOST }}:/home/data/firmware-pvs-studio-report/"${BRANCH_NAME}/${{steps.names.outputs.default_target}}-${{steps.names.outputs.suffix}}/"; | ||||||
|           rm ./deploy_key; |           rm ./deploy_key; | ||||||
| 
 | 
 | ||||||
|       - name: 'Find Previous Comment' |       - name: 'Find Previous Comment' | ||||||
|  | |||||||
							
								
								
									
										1
									
								
								.github/workflows/unit_tests.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						| @ -56,6 +56,7 @@ jobs: | |||||||
|       - name: 'Run units and validate results' |       - name: 'Run units and validate results' | ||||||
|         id: run_units |         id: run_units | ||||||
|         if: steps.copy.outcome == 'success' |         if: steps.copy.outcome == 'success' | ||||||
|  |         timeout-minutes: 2.5 | ||||||
|         run: | |         run: | | ||||||
|           source scripts/toolchain/fbtenv.sh |           source scripts/toolchain/fbtenv.sh | ||||||
|           python3 scripts/testing/units.py ${{steps.device.outputs.flipper}} |           python3 scripts/testing/units.py ${{steps.device.outputs.flipper}} | ||||||
|  | |||||||
							
								
								
									
										7
									
								
								.github/workflows/updater_test.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						| @ -10,7 +10,7 @@ env: | |||||||
| 
 | 
 | ||||||
| jobs: | jobs: | ||||||
|   test_updater_on_bench: |   test_updater_on_bench: | ||||||
|     runs-on: [self-hosted, FlipperZeroTest] # currently on same bench as units, needs different bench |     runs-on: [self-hosted, FlipperZeroTestMac1] | ||||||
|     steps: |     steps: | ||||||
|       - name: 'Decontaminate previous build leftovers' |       - name: 'Decontaminate previous build leftovers' | ||||||
|         run: | |         run: | | ||||||
| @ -27,7 +27,8 @@ jobs: | |||||||
|       - name: 'Get flipper from device manager (mock)' |       - name: 'Get flipper from device manager (mock)' | ||||||
|         id: device |         id: device | ||||||
|         run: | |         run: | | ||||||
|           echo "flipper=/dev/ttyACM0" >> $GITHUB_OUTPUT |           echo "flipper=/dev/tty.usbmodemflip_Rekigyn1" >> $GITHUB_OUTPUT | ||||||
|  |           echo "stlink=0F020D026415303030303032" >> $GITHUB_OUTPUT | ||||||
| 
 | 
 | ||||||
|       - name: 'Flashing target firmware' |       - name: 'Flashing target firmware' | ||||||
|         id: first_full_flash |         id: first_full_flash | ||||||
| @ -67,7 +68,7 @@ jobs: | |||||||
|       - name: 'Flash last release' |       - name: 'Flash last release' | ||||||
|         if: failure() |         if: failure() | ||||||
|         run: | |         run: | | ||||||
|           ./fbt flash OPENOCD_ADAPTER_SERIAL=2A0906016415303030303032 FORCE=1 |           ./fbt flash OPENOCD_ADAPTER_SERIAL=${{steps.device.outputs.stlink}} FORCE=1 | ||||||
| 
 | 
 | ||||||
|       - name: 'Wait for flipper and format ext' |       - name: 'Wait for flipper and format ext' | ||||||
|         if: failure() |         if: failure() | ||||||
|  | |||||||
| @ -1 +1 @@ | |||||||
| --rules-config .pvsconfig -e lib/fatfs -e lib/fnv1a-hash -e lib/FreeRTOS-Kernel -e lib/heatshrink -e lib/libusb_stm32 -e lib/littlefs -e lib/mbedtls -e lib/micro-ecc -e lib/microtar -e lib/mlib -e lib/qrcode -e lib/ST25RFAL002 -e lib/STM32CubeWB -e lib/u8g2 -e lib/nanopb -e */arm-none-eabi/* -e applications/plugins/dap_link/lib/free-dap | --ignore-ccache -C gccarm --rules-config .pvsconfig -e lib/fatfs -e lib/fnv1a-hash -e lib/FreeRTOS-Kernel -e lib/heatshrink -e lib/libusb_stm32 -e lib/littlefs -e lib/mbedtls -e lib/micro-ecc -e lib/microtar -e lib/mlib -e lib/qrcode -e lib/ST25RFAL002 -e lib/STM32CubeWB -e lib/u8g2 -e lib/nanopb -e */arm-none-eabi/* -e applications/plugins/dap_link/lib/free-dap | ||||||
|  | |||||||
							
								
								
									
										22
									
								
								.vscode/example/tasks.json
									
									
									
									
										vendored
									
									
								
							
							
						
						| @ -105,6 +105,12 @@ | |||||||
|             "type": "shell", |             "type": "shell", | ||||||
|             "command": "./fbt COMPACT=1 DEBUG=0 FORCE=1 flash_usb_full" |             "command": "./fbt COMPACT=1 DEBUG=0 FORCE=1 flash_usb_full" | ||||||
|         }, |         }, | ||||||
|  |         { | ||||||
|  |             "label": "[Debug] Create PVS-Studio report", | ||||||
|  |             "group": "build", | ||||||
|  |             "type": "shell", | ||||||
|  |             "command": "./fbt firmware_pvs" | ||||||
|  |         }, | ||||||
|         { |         { | ||||||
|             "label": "[Debug] Build FAPs", |             "label": "[Debug] Build FAPs", | ||||||
|             "group": "build", |             "group": "build", | ||||||
| @ -138,6 +144,18 @@ | |||||||
|                 "Serial Console" |                 "Serial Console" | ||||||
|             ] |             ] | ||||||
|         }, |         }, | ||||||
|  |         { | ||||||
|  |             "label": "[Debug] Build and upload all FAPs to Flipper over USB", | ||||||
|  |             "group": "build", | ||||||
|  |             "type": "shell", | ||||||
|  |             "command": "./fbt fap_deploy" | ||||||
|  |         }, | ||||||
|  |         { | ||||||
|  |             "label": "[Release] Build and upload all FAPs to Flipper over USB", | ||||||
|  |             "group": "build", | ||||||
|  |             "type": "shell", | ||||||
|  |             "command": "./fbt COMPACT=1 DEBUG=0 fap_deploy" | ||||||
|  |         }, | ||||||
|         { |         { | ||||||
|             // Press Ctrl+] to quit |             // Press Ctrl+] to quit | ||||||
|             "label": "Serial Console", |             "label": "Serial Console", | ||||||
| @ -145,7 +163,7 @@ | |||||||
|             "command": "./fbt cli", |             "command": "./fbt cli", | ||||||
|             "group": "none", |             "group": "none", | ||||||
|             "isBackground": true, |             "isBackground": true, | ||||||
| 			"options": { |             "options": { | ||||||
|                 "env": { |                 "env": { | ||||||
|                     "FBT_NO_SYNC": "0" |                     "FBT_NO_SYNC": "0" | ||||||
|                 } |                 } | ||||||
| @ -162,4 +180,4 @@ | |||||||
|             } |             } | ||||||
|         } |         } | ||||||
|     ] |     ] | ||||||
| } | } | ||||||
| @ -52,7 +52,7 @@ Almost everything in flipper firmware is built around this concept. | |||||||
| 
 | 
 | ||||||
| ## Naming | ## Naming | ||||||
| 
 | 
 | ||||||
| ### Type names are CamelCase | ### Type names are PascalCase | ||||||
| 
 | 
 | ||||||
| Examples: | Examples: | ||||||
| 
 | 
 | ||||||
|  | |||||||
							
								
								
									
										17
									
								
								SConstruct
									
									
									
									
									
								
							
							
						
						| @ -148,9 +148,12 @@ fap_dist = [ | |||||||
|             for app_artifact in firmware_env["FW_EXTAPPS"].applications.values() |             for app_artifact in firmware_env["FW_EXTAPPS"].applications.values() | ||||||
|         ), |         ), | ||||||
|     ), |     ), | ||||||
|     distenv.Install( |     *( | ||||||
|         f"#/dist/{dist_dir}/apps", |         distenv.Install( | ||||||
|         "#/assets/resources/apps", |             f"#/dist/{dist_dir}/apps/{app_artifact.app.fap_category}", | ||||||
|  |             app_artifact.compact[0], | ||||||
|  |         ) | ||||||
|  |         for app_artifact in firmware_env["FW_EXTAPPS"].applications.values() | ||||||
|     ), |     ), | ||||||
| ] | ] | ||||||
| Depends( | Depends( | ||||||
| @ -165,6 +168,14 @@ Alias("fap_dist", fap_dist) | |||||||
| 
 | 
 | ||||||
| distenv.Depends(firmware_env["FW_RESOURCES"], firmware_env["FW_EXTAPPS"].resources_dist) | distenv.Depends(firmware_env["FW_RESOURCES"], firmware_env["FW_EXTAPPS"].resources_dist) | ||||||
| 
 | 
 | ||||||
|  | # Copy all faps to device | ||||||
|  | 
 | ||||||
|  | fap_deploy = distenv.PhonyTarget( | ||||||
|  |     "fap_deploy", | ||||||
|  |     "${PYTHON3} ${ROOT_DIR}/scripts/storage.py send ${SOURCE} /ext/apps", | ||||||
|  |     source=Dir("#/assets/resources/apps"), | ||||||
|  | ) | ||||||
|  | 
 | ||||||
| 
 | 
 | ||||||
| # Target for bundling core2 package for qFlipper | # Target for bundling core2 package for qFlipper | ||||||
| copro_dist = distenv.CoproBuilder( | copro_dist = distenv.CoproBuilder( | ||||||
|  | |||||||
| @ -31,7 +31,8 @@ void AccessorApp::run(void) { | |||||||
|     onewire_host_stop(onewire_host); |     onewire_host_stop(onewire_host); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| AccessorApp::AccessorApp() { | AccessorApp::AccessorApp() | ||||||
|  |     : text_store{0} { | ||||||
|     notification = static_cast<NotificationApp*>(furi_record_open(RECORD_NOTIFICATION)); |     notification = static_cast<NotificationApp*>(furi_record_open(RECORD_NOTIFICATION)); | ||||||
|     onewire_host = onewire_host_alloc(); |     onewire_host = onewire_host_alloc(); | ||||||
|     furi_hal_power_enable_otg(); |     furi_hal_power_enable_otg(); | ||||||
|  | |||||||
| @ -171,9 +171,6 @@ bool WIEGAND::DoWiegandConversion() { | |||||||
|                     return true; |                     return true; | ||||||
|                 } else { |                 } else { | ||||||
|                     _lastWiegand = sysTick; |                     _lastWiegand = sysTick; | ||||||
|                     _bitCount = 0; |  | ||||||
|                     _cardTemp = 0; |  | ||||||
|                     _cardTempHigh = 0; |  | ||||||
|                     return false; |                     return false; | ||||||
|                 } |                 } | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -11,4 +11,5 @@ App( | |||||||
|     stack_size=1 * 1024, |     stack_size=1 * 1024, | ||||||
|     order=130, |     order=130, | ||||||
|     fap_category="Debug", |     fap_category="Debug", | ||||||
|  |     fap_libs=["assets"], | ||||||
| ) | ) | ||||||
|  | |||||||
							
								
								
									
										148
									
								
								applications/debug/battery_test_app/views/battery_info.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @ -0,0 +1,148 @@ | |||||||
|  | #include "battery_info.h" | ||||||
|  | #include <furi.h> | ||||||
|  | #include <gui/elements.h> | ||||||
|  | #include <assets_icons.h> | ||||||
|  | 
 | ||||||
|  | #define LOW_CHARGE_THRESHOLD 10 | ||||||
|  | #define HIGH_DRAIN_CURRENT_THRESHOLD 100 | ||||||
|  | 
 | ||||||
|  | struct BatteryInfo { | ||||||
|  |     View* view; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | static void draw_stat(Canvas* canvas, int x, int y, const Icon* icon, char* val) { | ||||||
|  |     canvas_draw_frame(canvas, x - 7, y + 7, 30, 13); | ||||||
|  |     canvas_draw_icon(canvas, x, y, icon); | ||||||
|  |     canvas_set_color(canvas, ColorWhite); | ||||||
|  |     canvas_draw_box(canvas, x - 4, y + 16, 24, 6); | ||||||
|  |     canvas_set_color(canvas, ColorBlack); | ||||||
|  |     canvas_draw_str_aligned(canvas, x + 8, y + 22, AlignCenter, AlignBottom, val); | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | static void draw_battery(Canvas* canvas, BatteryInfoModel* data, int x, int y) { | ||||||
|  |     char emote[20] = {}; | ||||||
|  |     char header[20] = {}; | ||||||
|  |     char value[20] = {}; | ||||||
|  | 
 | ||||||
|  |     int32_t drain_current = data->gauge_current * (-1000); | ||||||
|  |     uint32_t charge_current = data->gauge_current * 1000; | ||||||
|  | 
 | ||||||
|  |     // Draw battery
 | ||||||
|  |     canvas_draw_icon(canvas, x, y, &I_BatteryBody_52x28); | ||||||
|  |     if(charge_current > 0) { | ||||||
|  |         canvas_draw_icon(canvas, x + 16, y + 7, &I_FaceCharging_29x14); | ||||||
|  |     } else if(drain_current > HIGH_DRAIN_CURRENT_THRESHOLD) { | ||||||
|  |         canvas_draw_icon(canvas, x + 16, y + 7, &I_FaceConfused_29x14); | ||||||
|  |     } else if(data->charge < LOW_CHARGE_THRESHOLD) { | ||||||
|  |         canvas_draw_icon(canvas, x + 16, y + 7, &I_FaceNopower_29x14); | ||||||
|  |     } else { | ||||||
|  |         canvas_draw_icon(canvas, x + 16, y + 7, &I_FaceNormal_29x14); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     // Draw bubble
 | ||||||
|  |     elements_bubble(canvas, 53, 0, 71, 39); | ||||||
|  | 
 | ||||||
|  |     // Set text
 | ||||||
|  |     if(charge_current > 0) { | ||||||
|  |         snprintf(emote, sizeof(emote), "%s", "Yummy!"); | ||||||
|  |         snprintf(header, sizeof(header), "%s", "Charging at"); | ||||||
|  |         snprintf( | ||||||
|  |             value, | ||||||
|  |             sizeof(value), | ||||||
|  |             "%lu.%luV   %lumA", | ||||||
|  |             (uint32_t)(data->vbus_voltage), | ||||||
|  |             (uint32_t)(data->vbus_voltage * 10) % 10, | ||||||
|  |             charge_current); | ||||||
|  |     } else if(drain_current > 0) { | ||||||
|  |         snprintf( | ||||||
|  |             emote, | ||||||
|  |             sizeof(emote), | ||||||
|  |             "%s", | ||||||
|  |             drain_current > HIGH_DRAIN_CURRENT_THRESHOLD ? "Oh no!" : "Om-nom-nom!"); | ||||||
|  |         snprintf(header, sizeof(header), "%s", "Consumption is"); | ||||||
|  |         snprintf( | ||||||
|  |             value, | ||||||
|  |             sizeof(value), | ||||||
|  |             "%ld %s", | ||||||
|  |             drain_current, | ||||||
|  |             drain_current > HIGH_DRAIN_CURRENT_THRESHOLD ? "mA!" : "mA"); | ||||||
|  |     } else if(drain_current != 0) { | ||||||
|  |         snprintf(header, 20, "..."); | ||||||
|  |     } else if(data->charging_voltage < 4.2) { | ||||||
|  |         // Non-default battery charging limit, mention it
 | ||||||
|  |         snprintf(emote, sizeof(emote), "Charged!"); | ||||||
|  |         snprintf(header, sizeof(header), "Limited to"); | ||||||
|  |         snprintf( | ||||||
|  |             value, | ||||||
|  |             sizeof(value), | ||||||
|  |             "%lu.%luV", | ||||||
|  |             (uint32_t)(data->charging_voltage), | ||||||
|  |             (uint32_t)(data->charging_voltage * 10) % 10); | ||||||
|  |     } else { | ||||||
|  |         snprintf(header, sizeof(header), "Charged!"); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     canvas_draw_str_aligned(canvas, 92, y + 3, AlignCenter, AlignCenter, emote); | ||||||
|  |     canvas_draw_str_aligned(canvas, 92, y + 15, AlignCenter, AlignCenter, header); | ||||||
|  |     canvas_draw_str_aligned(canvas, 92, y + 27, AlignCenter, AlignCenter, value); | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | static void battery_info_draw_callback(Canvas* canvas, void* context) { | ||||||
|  |     furi_assert(context); | ||||||
|  |     BatteryInfoModel* model = context; | ||||||
|  | 
 | ||||||
|  |     canvas_clear(canvas); | ||||||
|  |     canvas_set_color(canvas, ColorBlack); | ||||||
|  |     draw_battery(canvas, model, 0, 5); | ||||||
|  | 
 | ||||||
|  |     char batt_level[10]; | ||||||
|  |     char temperature[10]; | ||||||
|  |     char voltage[10]; | ||||||
|  |     char health[10]; | ||||||
|  | 
 | ||||||
|  |     snprintf(batt_level, sizeof(batt_level), "%lu%%", (uint32_t)model->charge); | ||||||
|  |     snprintf(temperature, sizeof(temperature), "%lu C", (uint32_t)model->gauge_temperature); | ||||||
|  |     snprintf( | ||||||
|  |         voltage, | ||||||
|  |         sizeof(voltage), | ||||||
|  |         "%lu.%01lu V", | ||||||
|  |         (uint32_t)model->gauge_voltage, | ||||||
|  |         (uint32_t)(model->gauge_voltage * 10) % 10UL); | ||||||
|  |     snprintf(health, sizeof(health), "%d%%", model->health); | ||||||
|  | 
 | ||||||
|  |     draw_stat(canvas, 8, 42, &I_Battery_16x16, batt_level); | ||||||
|  |     draw_stat(canvas, 40, 42, &I_Temperature_16x16, temperature); | ||||||
|  |     draw_stat(canvas, 72, 42, &I_Voltage_16x16, voltage); | ||||||
|  |     draw_stat(canvas, 104, 42, &I_Health_16x16, health); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | BatteryInfo* battery_info_alloc() { | ||||||
|  |     BatteryInfo* battery_info = malloc(sizeof(BatteryInfo)); | ||||||
|  |     battery_info->view = view_alloc(); | ||||||
|  |     view_set_context(battery_info->view, battery_info); | ||||||
|  |     view_allocate_model(battery_info->view, ViewModelTypeLocking, sizeof(BatteryInfoModel)); | ||||||
|  |     view_set_draw_callback(battery_info->view, battery_info_draw_callback); | ||||||
|  | 
 | ||||||
|  |     return battery_info; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void battery_info_free(BatteryInfo* battery_info) { | ||||||
|  |     furi_assert(battery_info); | ||||||
|  |     view_free(battery_info->view); | ||||||
|  |     free(battery_info); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | View* battery_info_get_view(BatteryInfo* battery_info) { | ||||||
|  |     furi_assert(battery_info); | ||||||
|  |     return battery_info->view; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void battery_info_set_data(BatteryInfo* battery_info, BatteryInfoModel* data) { | ||||||
|  |     furi_assert(battery_info); | ||||||
|  |     furi_assert(data); | ||||||
|  |     with_view_model( | ||||||
|  |         battery_info->view, | ||||||
|  |         BatteryInfoModel * model, | ||||||
|  |         { memcpy(model, data, sizeof(BatteryInfoModel)); }, | ||||||
|  |         true); | ||||||
|  | } | ||||||
							
								
								
									
										23
									
								
								applications/debug/battery_test_app/views/battery_info.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @ -0,0 +1,23 @@ | |||||||
|  | #pragma once | ||||||
|  | 
 | ||||||
|  | #include <gui/view.h> | ||||||
|  | 
 | ||||||
|  | typedef struct BatteryInfo BatteryInfo; | ||||||
|  | 
 | ||||||
|  | typedef struct { | ||||||
|  |     float vbus_voltage; | ||||||
|  |     float gauge_voltage; | ||||||
|  |     float gauge_current; | ||||||
|  |     float gauge_temperature; | ||||||
|  |     float charging_voltage; | ||||||
|  |     uint8_t charge; | ||||||
|  |     uint8_t health; | ||||||
|  | } BatteryInfoModel; | ||||||
|  | 
 | ||||||
|  | BatteryInfo* battery_info_alloc(); | ||||||
|  | 
 | ||||||
|  | void battery_info_free(BatteryInfo* battery_info); | ||||||
|  | 
 | ||||||
|  | View* battery_info_get_view(BatteryInfo* battery_info); | ||||||
|  | 
 | ||||||
|  | void battery_info_set_data(BatteryInfo* battery_info, BatteryInfoModel* data); | ||||||
| @ -31,9 +31,6 @@ uint32_t bt_debug_start_view(void* context) { | |||||||
| BtDebugApp* bt_debug_app_alloc() { | BtDebugApp* bt_debug_app_alloc() { | ||||||
|     BtDebugApp* app = malloc(sizeof(BtDebugApp)); |     BtDebugApp* app = malloc(sizeof(BtDebugApp)); | ||||||
| 
 | 
 | ||||||
|     // Load settings
 |  | ||||||
|     bt_settings_load(&app->settings); |  | ||||||
| 
 |  | ||||||
|     // Gui
 |     // Gui
 | ||||||
|     app->gui = furi_record_open(RECORD_GUI); |     app->gui = furi_record_open(RECORD_GUI); | ||||||
| 
 | 
 | ||||||
| @ -105,13 +102,15 @@ int32_t bt_debug_app(void* p) { | |||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     BtDebugApp* app = bt_debug_app_alloc(); |     BtDebugApp* app = bt_debug_app_alloc(); | ||||||
|  |     // Was bt active?
 | ||||||
|  |     const bool was_active = furi_hal_bt_is_active(); | ||||||
|     // Stop advertising
 |     // Stop advertising
 | ||||||
|     furi_hal_bt_stop_advertising(); |     furi_hal_bt_stop_advertising(); | ||||||
| 
 | 
 | ||||||
|     view_dispatcher_run(app->view_dispatcher); |     view_dispatcher_run(app->view_dispatcher); | ||||||
| 
 | 
 | ||||||
|     // Restart advertising
 |     // Restart advertising
 | ||||||
|     if(app->settings.enabled) { |     if(was_active) { | ||||||
|         furi_hal_bt_start_advertising(); |         furi_hal_bt_start_advertising(); | ||||||
|     } |     } | ||||||
|     bt_debug_app_free(app); |     bt_debug_app_free(app); | ||||||
|  | |||||||
| @ -4,15 +4,14 @@ | |||||||
| #include <gui/gui.h> | #include <gui/gui.h> | ||||||
| #include <gui/view.h> | #include <gui/view.h> | ||||||
| #include <gui/view_dispatcher.h> | #include <gui/view_dispatcher.h> | ||||||
|  | #include <gui/modules/submenu.h> | ||||||
|  | 
 | ||||||
| #include <dialogs/dialogs.h> | #include <dialogs/dialogs.h> | ||||||
| 
 | 
 | ||||||
| #include <gui/modules/submenu.h> |  | ||||||
| #include "views/bt_carrier_test.h" | #include "views/bt_carrier_test.h" | ||||||
| #include "views/bt_packet_test.h" | #include "views/bt_packet_test.h" | ||||||
| #include <bt/bt_settings.h> |  | ||||||
| 
 | 
 | ||||||
| typedef struct { | typedef struct { | ||||||
|     BtSettings settings; |  | ||||||
|     Gui* gui; |     Gui* gui; | ||||||
|     ViewDispatcher* view_dispatcher; |     ViewDispatcher* view_dispatcher; | ||||||
|     Submenu* submenu; |     Submenu* submenu; | ||||||
|  | |||||||
| @ -2,8 +2,11 @@ | |||||||
| 
 | 
 | ||||||
| #include <gui/canvas.h> | #include <gui/canvas.h> | ||||||
| #include <gui/elements.h> | #include <gui/elements.h> | ||||||
|  | 
 | ||||||
|  | #include <lib/toolbox/float_tools.h> | ||||||
| #include <m-array.h> | #include <m-array.h> | ||||||
| #include <furi.h> | #include <furi.h> | ||||||
|  | #include <inttypes.h> | ||||||
| #include <stdint.h> | #include <stdint.h> | ||||||
| 
 | 
 | ||||||
| struct BtTestParam { | struct BtTestParam { | ||||||
| @ -98,16 +101,16 @@ static void bt_test_draw_callback(Canvas* canvas, void* _model) { | |||||||
|     elements_scrollbar(canvas, model->position, BtTestParamArray_size(model->params)); |     elements_scrollbar(canvas, model->position, BtTestParamArray_size(model->params)); | ||||||
|     canvas_draw_str(canvas, 6, 60, model->message); |     canvas_draw_str(canvas, 6, 60, model->message); | ||||||
|     if(model->state == BtTestStateStarted) { |     if(model->state == BtTestStateStarted) { | ||||||
|         if(model->rssi != 0.0f) { |         if(!float_is_equal(model->rssi, 0.0f)) { | ||||||
|             snprintf(info_str, sizeof(info_str), "RSSI:%3.1f dB", (double)model->rssi); |             snprintf(info_str, sizeof(info_str), "RSSI:%3.1f dB", (double)model->rssi); | ||||||
|             canvas_draw_str_aligned(canvas, 124, 60, AlignRight, AlignBottom, info_str); |             canvas_draw_str_aligned(canvas, 124, 60, AlignRight, AlignBottom, info_str); | ||||||
|         } |         } | ||||||
|     } else if(model->state == BtTestStateStopped) { |     } else if(model->state == BtTestStateStopped) { | ||||||
|         if(model->packets_num_rx) { |         if(model->packets_num_rx) { | ||||||
|             snprintf(info_str, sizeof(info_str), "%ld pack rcv", model->packets_num_rx); |             snprintf(info_str, sizeof(info_str), "%" PRIu32 " pack rcv", model->packets_num_rx); | ||||||
|             canvas_draw_str_aligned(canvas, 124, 60, AlignRight, AlignBottom, info_str); |             canvas_draw_str_aligned(canvas, 124, 60, AlignRight, AlignBottom, info_str); | ||||||
|         } else if(model->packets_num_tx) { |         } else if(model->packets_num_tx) { | ||||||
|             snprintf(info_str, sizeof(info_str), "%ld pack sent", model->packets_num_tx); |             snprintf(info_str, sizeof(info_str), "%" PRIu32 " pack sent", model->packets_num_tx); | ||||||
|             canvas_draw_str_aligned(canvas, 124, 60, AlignRight, AlignBottom, info_str); |             canvas_draw_str_aligned(canvas, 124, 60, AlignRight, AlignBottom, info_str); | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| @ -153,7 +156,7 @@ static bool bt_test_input_callback(InputEvent* event, void* context) { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void bt_test_process_up(BtTest* bt_test) { | void bt_test_process_up(BtTest* bt_test) { | ||||||
|     with_view_model( |     with_view_model( // -V658
 | ||||||
|         bt_test->view, |         bt_test->view, | ||||||
|         BtTestModel * model, |         BtTestModel * model, | ||||||
|         { |         { | ||||||
|  | |||||||
| @ -5,6 +5,7 @@ App( | |||||||
|     entry_point="display_test_app", |     entry_point="display_test_app", | ||||||
|     cdefines=["APP_DISPLAY_TEST"], |     cdefines=["APP_DISPLAY_TEST"], | ||||||
|     requires=["gui"], |     requires=["gui"], | ||||||
|  |     fap_libs=["misc"], | ||||||
|     stack_size=1 * 1024, |     stack_size=1 * 1024, | ||||||
|     order=120, |     order=120, | ||||||
|     fap_category="Debug", |     fap_category="Debug", | ||||||
|  | |||||||
| @ -91,7 +91,6 @@ static void display_test_reload_config(DisplayTest* instance) { | |||||||
|         instance->config_contrast, |         instance->config_contrast, | ||||||
|         instance->config_regulation_ratio, |         instance->config_regulation_ratio, | ||||||
|         instance->config_bias); |         instance->config_bias); | ||||||
|     gui_update(instance->gui); |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static void display_config_set_bias(VariableItem* item) { | static void display_config_set_bias(VariableItem* item) { | ||||||
|  | |||||||
| @ -48,7 +48,7 @@ FileBrowserApp* file_browser_app_alloc(char* arg) { | |||||||
| 
 | 
 | ||||||
|     app->file_path = furi_string_alloc(); |     app->file_path = furi_string_alloc(); | ||||||
|     app->file_browser = file_browser_alloc(app->file_path); |     app->file_browser = file_browser_alloc(app->file_path); | ||||||
|     file_browser_configure(app->file_browser, "*", NULL, true, &I_badusb_10px, true); |     file_browser_configure(app->file_browser, "*", NULL, true, false, &I_badusb_10px, true); | ||||||
| 
 | 
 | ||||||
|     view_dispatcher_add_view( |     view_dispatcher_add_view( | ||||||
|         app->view_dispatcher, FileBrowserAppViewStart, widget_get_view(app->widget)); |         app->view_dispatcher, FileBrowserAppViewStart, widget_get_view(app->widget)); | ||||||
|  | |||||||
| @ -44,7 +44,11 @@ bool rpc_debug_app_scene_input_error_code_on_event(void* context, SceneManagerEv | |||||||
| 
 | 
 | ||||||
|     if(event.type == SceneManagerEventTypeCustom) { |     if(event.type == SceneManagerEventTypeCustom) { | ||||||
|         if(event.event == RpcDebugAppCustomEventInputErrorCode) { |         if(event.event == RpcDebugAppCustomEventInputErrorCode) { | ||||||
|             rpc_system_app_set_error_code(app->rpc, (uint32_t)atol(app->text_store)); |             char* end; | ||||||
|  |             int error_code = strtol(app->text_store, &end, 10); | ||||||
|  |             if(!*end) { | ||||||
|  |                 rpc_system_app_set_error_code(app->rpc, error_code); | ||||||
|  |             } | ||||||
|             scene_manager_previous_scene(app->scene_manager); |             scene_manager_previous_scene(app->scene_manager); | ||||||
|             consumed = true; |             consumed = true; | ||||||
|         } |         } | ||||||
|  | |||||||
| @ -466,6 +466,10 @@ static void mf_classic_generator_test(uint8_t uid_len, MfClassicType type) { | |||||||
|     nfc_device_free(nfc_keys); |     nfc_device_free(nfc_keys); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | MU_TEST(mf_mini_file_test) { | ||||||
|  |     mf_classic_generator_test(4, MfClassicTypeMini); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| MU_TEST(mf_classic_1k_4b_file_test) { | MU_TEST(mf_classic_1k_4b_file_test) { | ||||||
|     mf_classic_generator_test(4, MfClassicType1k); |     mf_classic_generator_test(4, MfClassicType1k); | ||||||
| } | } | ||||||
| @ -486,6 +490,7 @@ MU_TEST_SUITE(nfc) { | |||||||
|     nfc_test_alloc(); |     nfc_test_alloc(); | ||||||
| 
 | 
 | ||||||
|     MU_RUN_TEST(nfca_file_test); |     MU_RUN_TEST(nfca_file_test); | ||||||
|  |     MU_RUN_TEST(mf_mini_file_test); | ||||||
|     MU_RUN_TEST(mf_classic_1k_4b_file_test); |     MU_RUN_TEST(mf_classic_1k_4b_file_test); | ||||||
|     MU_RUN_TEST(mf_classic_4k_4b_file_test); |     MU_RUN_TEST(mf_classic_4k_4b_file_test); | ||||||
|     MU_RUN_TEST(mf_classic_1k_7b_file_test); |     MU_RUN_TEST(mf_classic_1k_7b_file_test); | ||||||
|  | |||||||
| @ -1,5 +1,5 @@ | |||||||
| App( | App( | ||||||
|     appid="sample_apps", |     appid="example_apps", | ||||||
|     name="Sample apps bundle", |     name="Example apps bundle", | ||||||
|     apptype=FlipperAppType.METAPACKAGE, |     apptype=FlipperAppType.METAPACKAGE, | ||||||
| ) | ) | ||||||
|  | |||||||
| @ -50,6 +50,7 @@ static const DuckyKey ducky_keys[] = { | |||||||
|     {"ALT-SHIFT", KEY_MOD_LEFT_ALT | KEY_MOD_LEFT_SHIFT}, |     {"ALT-SHIFT", KEY_MOD_LEFT_ALT | KEY_MOD_LEFT_SHIFT}, | ||||||
|     {"ALT-GUI", KEY_MOD_LEFT_ALT | KEY_MOD_LEFT_GUI}, |     {"ALT-GUI", KEY_MOD_LEFT_ALT | KEY_MOD_LEFT_GUI}, | ||||||
|     {"GUI-SHIFT", KEY_MOD_LEFT_GUI | KEY_MOD_LEFT_SHIFT}, |     {"GUI-SHIFT", KEY_MOD_LEFT_GUI | KEY_MOD_LEFT_SHIFT}, | ||||||
|  |     {"GUI-CTRL", KEY_MOD_LEFT_GUI | KEY_MOD_LEFT_CTRL}, | ||||||
| 
 | 
 | ||||||
|     {"CTRL", KEY_MOD_LEFT_CTRL}, |     {"CTRL", KEY_MOD_LEFT_CTRL}, | ||||||
|     {"CONTROL", KEY_MOD_LEFT_CTRL}, |     {"CONTROL", KEY_MOD_LEFT_CTRL}, | ||||||
| @ -71,8 +72,8 @@ static const DuckyKey ducky_keys[] = { | |||||||
|     {"BREAK", HID_KEYBOARD_PAUSE}, |     {"BREAK", HID_KEYBOARD_PAUSE}, | ||||||
|     {"PAUSE", HID_KEYBOARD_PAUSE}, |     {"PAUSE", HID_KEYBOARD_PAUSE}, | ||||||
|     {"CAPSLOCK", HID_KEYBOARD_CAPS_LOCK}, |     {"CAPSLOCK", HID_KEYBOARD_CAPS_LOCK}, | ||||||
|     {"DELETE", HID_KEYBOARD_DELETE}, |     {"DELETE", HID_KEYBOARD_DELETE_FORWARD}, | ||||||
|     {"BACKSPACE", HID_KEYPAD_BACKSPACE}, |     {"BACKSPACE", HID_KEYBOARD_DELETE}, | ||||||
|     {"END", HID_KEYBOARD_END}, |     {"END", HID_KEYBOARD_END}, | ||||||
|     {"ESC", HID_KEYBOARD_ESCAPE}, |     {"ESC", HID_KEYBOARD_ESCAPE}, | ||||||
|     {"ESCAPE", HID_KEYBOARD_ESCAPE}, |     {"ESCAPE", HID_KEYBOARD_ESCAPE}, | ||||||
|  | |||||||
| @ -14,9 +14,12 @@ static const char* uart_ch[] = {"13,14", "15,16"}; | |||||||
| static const char* flow_pins[] = {"None", "2,3", "6,7", "16,15"}; | static const char* flow_pins[] = {"None", "2,3", "6,7", "16,15"}; | ||||||
| static const char* baudrate_mode[] = {"Host"}; | static const char* baudrate_mode[] = {"Host"}; | ||||||
| static const uint32_t baudrate_list[] = { | static const uint32_t baudrate_list[] = { | ||||||
|  |     1200, | ||||||
|     2400, |     2400, | ||||||
|  |     4800, | ||||||
|     9600, |     9600, | ||||||
|     19200, |     19200, | ||||||
|  |     28800, | ||||||
|     38400, |     38400, | ||||||
|     57600, |     57600, | ||||||
|     115200, |     115200, | ||||||
|  | |||||||
| @ -394,8 +394,8 @@ int32_t snake_game_app(void* p) { | |||||||
|         release_mutex(&state_mutex, snake_state); |         release_mutex(&state_mutex, snake_state); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     // Wait for all notifications to be played and return backlight to normal state
 |     // Return backlight to normal state
 | ||||||
|     notification_message_block(notification, &sequence_display_backlight_enforce_auto); |     notification_message(notification, &sequence_display_backlight_enforce_auto); | ||||||
| 
 | 
 | ||||||
|     furi_timer_free(timer); |     furi_timer_free(timer); | ||||||
|     view_port_enabled_set(view_port, false); |     view_port_enabled_set(view_port, false); | ||||||
|  | |||||||
| @ -3,7 +3,7 @@ | |||||||
| #include <furi.h> | #include <furi.h> | ||||||
| #include <furi_hal.h> | #include <furi_hal.h> | ||||||
| 
 | 
 | ||||||
| #define WS_VERSION_APP "0.6.1" | #define WS_VERSION_APP "0.7" | ||||||
| #define WS_DEVELOPED "SkorP" | #define WS_DEVELOPED "SkorP" | ||||||
| #define WS_GITHUB "https://github.com/flipperdevices/flipperzero-firmware"
 | #define WS_GITHUB "https://github.com/flipperdevices/flipperzero-firmware"
 | ||||||
| 
 | 
 | ||||||
|  | |||||||
							
								
								
									
										329
									
								
								applications/plugins/weather_station/protocols/lacrosse_tx.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @ -0,0 +1,329 @@ | |||||||
|  | #include "lacrosse_tx.h" | ||||||
|  | 
 | ||||||
|  | #define TAG "WSProtocolLaCrosse_TX" | ||||||
|  | 
 | ||||||
|  | /*
 | ||||||
|  |  * Help | ||||||
|  |  * https://github.com/merbanan/rtl_433/blob/master/src/devices/lacrosse.c
 | ||||||
|  |  *   | ||||||
|  |  *  | ||||||
|  |  * LaCrosse TX 433 Mhz Temperature and Humidity Sensors. | ||||||
|  |  * - Tested: TX-7U and TX-6U (Temperature only) | ||||||
|  |  * - Not Tested but should work: TX-3, TX-4 | ||||||
|  |  * - also TFA Dostmann 30.3120.90 sensor (for e.g. 35.1018.06 (WS-9015) station) | ||||||
|  |  * - also TFA Dostmann 30.3121 sensor | ||||||
|  |  * Protocol Documentation: http://www.f6fbb.org/domo/sensors/tx3_th.php
 | ||||||
|  |  * Message is 44 bits, 11 x 4 bit nybbles: | ||||||
|  |  *    [00] [cnt = 10] [type] [addr] [addr + parity] [v1] [v2] [v3] [iv1] [iv2] [check] | ||||||
|  |  * Notes: | ||||||
|  |  * - Zero Pulses are longer (1,400 uS High, 1,000 uS Low) = 2,400 uS | ||||||
|  |  * - One Pulses are shorter (  550 uS High, 1,000 uS Low) = 1,600 uS | ||||||
|  |  * - Sensor id changes when the battery is changed | ||||||
|  |  * - Primary Value are BCD with one decimal place: vvv = 12.3 | ||||||
|  |  * - Secondary value is integer only intval = 12, seems to be a repeat of primary | ||||||
|  |  *   This may actually be an additional data check because the 4 bit checksum | ||||||
|  |  *   and parity bit is  pretty week at detecting errors. | ||||||
|  |  * - Temperature is in Celsius with 50.0 added (to handle negative values) | ||||||
|  |  * - Humidity values appear to be integer precision, decimal always 0. | ||||||
|  |  * - There is a 4 bit checksum and a parity bit covering the three digit value | ||||||
|  |  * - Parity check for TX-3 and TX-4 might be different. | ||||||
|  |  * - Msg sent with one repeat after 30 mS | ||||||
|  |  * - Temperature and humidity are sent as separate messages | ||||||
|  |  * - Frequency for each sensor may be could be off by as much as 50-75 khz | ||||||
|  |  * - LaCrosse Sensors in other frequency ranges (915 Mhz) use FSK not OOK | ||||||
|  |  *   so they can't be decoded by rtl_433 currently. | ||||||
|  |  * - Temperature and Humidity are sent in different messages bursts. | ||||||
|  | */ | ||||||
|  | 
 | ||||||
|  | #define LACROSSE_TX_GAP 1000 | ||||||
|  | #define LACROSSE_TX_BIT_SIZE 44 | ||||||
|  | #define LACROSSE_TX_SUNC_PATTERN 0x0A000000000 | ||||||
|  | #define LACROSSE_TX_SUNC_MASK 0x0F000000000 | ||||||
|  | #define LACROSSE_TX_MSG_TYPE_TEMP 0x00 | ||||||
|  | #define LACROSSE_TX_MSG_TYPE_HUM 0x0E | ||||||
|  | 
 | ||||||
|  | static const SubGhzBlockConst ws_protocol_lacrosse_tx_const = { | ||||||
|  |     .te_short = 550, | ||||||
|  |     .te_long = 1300, | ||||||
|  |     .te_delta = 120, | ||||||
|  |     .min_count_bit_for_found = 40, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | struct WSProtocolDecoderLaCrosse_TX { | ||||||
|  |     SubGhzProtocolDecoderBase base; | ||||||
|  | 
 | ||||||
|  |     SubGhzBlockDecoder decoder; | ||||||
|  |     WSBlockGeneric generic; | ||||||
|  | 
 | ||||||
|  |     uint16_t header_count; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | struct WSProtocolEncoderLaCrosse_TX { | ||||||
|  |     SubGhzProtocolEncoderBase base; | ||||||
|  | 
 | ||||||
|  |     SubGhzProtocolBlockEncoder encoder; | ||||||
|  |     WSBlockGeneric generic; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | typedef enum { | ||||||
|  |     LaCrosse_TXDecoderStepReset = 0, | ||||||
|  |     LaCrosse_TXDecoderStepCheckPreambule, | ||||||
|  |     LaCrosse_TXDecoderStepSaveDuration, | ||||||
|  |     LaCrosse_TXDecoderStepCheckDuration, | ||||||
|  | } LaCrosse_TXDecoderStep; | ||||||
|  | 
 | ||||||
|  | const SubGhzProtocolDecoder ws_protocol_lacrosse_tx_decoder = { | ||||||
|  |     .alloc = ws_protocol_decoder_lacrosse_tx_alloc, | ||||||
|  |     .free = ws_protocol_decoder_lacrosse_tx_free, | ||||||
|  | 
 | ||||||
|  |     .feed = ws_protocol_decoder_lacrosse_tx_feed, | ||||||
|  |     .reset = ws_protocol_decoder_lacrosse_tx_reset, | ||||||
|  | 
 | ||||||
|  |     .get_hash_data = ws_protocol_decoder_lacrosse_tx_get_hash_data, | ||||||
|  |     .serialize = ws_protocol_decoder_lacrosse_tx_serialize, | ||||||
|  |     .deserialize = ws_protocol_decoder_lacrosse_tx_deserialize, | ||||||
|  |     .get_string = ws_protocol_decoder_lacrosse_tx_get_string, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | const SubGhzProtocolEncoder ws_protocol_lacrosse_tx_encoder = { | ||||||
|  |     .alloc = NULL, | ||||||
|  |     .free = NULL, | ||||||
|  | 
 | ||||||
|  |     .deserialize = NULL, | ||||||
|  |     .stop = NULL, | ||||||
|  |     .yield = NULL, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | const SubGhzProtocol ws_protocol_lacrosse_tx = { | ||||||
|  |     .name = WS_PROTOCOL_LACROSSE_TX_NAME, | ||||||
|  |     .type = SubGhzProtocolWeatherStation, | ||||||
|  |     .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_315 | SubGhzProtocolFlag_868 | | ||||||
|  |             SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable, | ||||||
|  | 
 | ||||||
|  |     .decoder = &ws_protocol_lacrosse_tx_decoder, | ||||||
|  |     .encoder = &ws_protocol_lacrosse_tx_encoder, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | void* ws_protocol_decoder_lacrosse_tx_alloc(SubGhzEnvironment* environment) { | ||||||
|  |     UNUSED(environment); | ||||||
|  |     WSProtocolDecoderLaCrosse_TX* instance = malloc(sizeof(WSProtocolDecoderLaCrosse_TX)); | ||||||
|  |     instance->base.protocol = &ws_protocol_lacrosse_tx; | ||||||
|  |     instance->generic.protocol_name = instance->base.protocol->name; | ||||||
|  |     return instance; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void ws_protocol_decoder_lacrosse_tx_free(void* context) { | ||||||
|  |     furi_assert(context); | ||||||
|  |     WSProtocolDecoderLaCrosse_TX* instance = context; | ||||||
|  |     free(instance); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void ws_protocol_decoder_lacrosse_tx_reset(void* context) { | ||||||
|  |     furi_assert(context); | ||||||
|  |     WSProtocolDecoderLaCrosse_TX* instance = context; | ||||||
|  |     instance->header_count = 0; | ||||||
|  |     instance->decoder.parser_step = LaCrosse_TXDecoderStepReset; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static bool ws_protocol_lacrosse_tx_check_crc(WSProtocolDecoderLaCrosse_TX* instance) { | ||||||
|  |     if(!instance->decoder.decode_data) return false; | ||||||
|  |     uint8_t msg[] = { | ||||||
|  |         (instance->decoder.decode_data >> 36) & 0x0F, | ||||||
|  |         (instance->decoder.decode_data >> 32) & 0x0F, | ||||||
|  |         (instance->decoder.decode_data >> 28) & 0x0F, | ||||||
|  |         (instance->decoder.decode_data >> 24) & 0x0F, | ||||||
|  |         (instance->decoder.decode_data >> 20) & 0x0F, | ||||||
|  |         (instance->decoder.decode_data >> 16) & 0x0F, | ||||||
|  |         (instance->decoder.decode_data >> 12) & 0x0F, | ||||||
|  |         (instance->decoder.decode_data >> 8) & 0x0F, | ||||||
|  |         (instance->decoder.decode_data >> 4) & 0x0F}; | ||||||
|  | 
 | ||||||
|  |     uint8_t crc = subghz_protocol_blocks_add_bytes(msg, 9); | ||||||
|  |     return ((crc & 0x0F) == ((instance->decoder.decode_data) & 0x0F)); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Analysis of received data | ||||||
|  |  * @param instance Pointer to a WSBlockGeneric* instance | ||||||
|  |  */ | ||||||
|  | static void ws_protocol_lacrosse_tx_remote_controller(WSBlockGeneric* instance) { | ||||||
|  |     uint8_t msg_type = (instance->data >> 32) & 0x0F; | ||||||
|  |     instance->id = (((instance->data >> 28) & 0x0F) << 3) | (((instance->data >> 24) & 0x0F) >> 1); | ||||||
|  | 
 | ||||||
|  |     float msg_value = (float)((instance->data >> 20) & 0x0F) * 10.0f + | ||||||
|  |                       (float)((instance->data >> 16) & 0x0F) + | ||||||
|  |                       (float)((instance->data >> 12) & 0x0F) * 0.1f; | ||||||
|  | 
 | ||||||
|  |     if(msg_type == LACROSSE_TX_MSG_TYPE_TEMP) { //-V1051
 | ||||||
|  |         instance->temp = msg_value - 50.0f; | ||||||
|  |         instance->humidity = WS_NO_HUMIDITY; | ||||||
|  |     } else if(msg_type == LACROSSE_TX_MSG_TYPE_HUM) { | ||||||
|  |         //ToDo for verification, records are needed with sensors maintaining temperature and temperature for this standard
 | ||||||
|  |         instance->humidity = (uint8_t)msg_value; | ||||||
|  |     } else { | ||||||
|  |         furi_crash("WS: WSProtocolLaCrosse_TX incorrect msg_type."); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     instance->btn = WS_NO_BTN; | ||||||
|  |     instance->battery_low = WS_NO_BATT; | ||||||
|  |     instance->channel = WS_NO_CHANNEL; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void ws_protocol_decoder_lacrosse_tx_feed(void* context, bool level, uint32_t duration) { | ||||||
|  |     furi_assert(context); | ||||||
|  |     WSProtocolDecoderLaCrosse_TX* instance = context; | ||||||
|  | 
 | ||||||
|  |     switch(instance->decoder.parser_step) { | ||||||
|  |     case LaCrosse_TXDecoderStepReset: | ||||||
|  |         if((!level) && (DURATION_DIFF(duration, LACROSSE_TX_GAP) < | ||||||
|  |                         ws_protocol_lacrosse_tx_const.te_delta * 2)) { | ||||||
|  |             instance->decoder.parser_step = LaCrosse_TXDecoderStepCheckPreambule; | ||||||
|  |             instance->header_count = 0; | ||||||
|  |         } | ||||||
|  |         break; | ||||||
|  | 
 | ||||||
|  |     case LaCrosse_TXDecoderStepCheckPreambule: | ||||||
|  | 
 | ||||||
|  |         if(level) { | ||||||
|  |             if((DURATION_DIFF(duration, ws_protocol_lacrosse_tx_const.te_short) < | ||||||
|  |                 ws_protocol_lacrosse_tx_const.te_delta) && | ||||||
|  |                (instance->header_count > 1)) { | ||||||
|  |                 instance->decoder.parser_step = LaCrosse_TXDecoderStepCheckDuration; | ||||||
|  |                 instance->decoder.decode_data = 0; | ||||||
|  |                 instance->decoder.decode_count_bit = 0; | ||||||
|  |                 instance->decoder.te_last = duration; | ||||||
|  |             } else if(duration > (ws_protocol_lacrosse_tx_const.te_long * 2)) { | ||||||
|  |                 instance->decoder.parser_step = LaCrosse_TXDecoderStepReset; | ||||||
|  |             } | ||||||
|  |         } else { | ||||||
|  |             if(DURATION_DIFF(duration, LACROSSE_TX_GAP) < | ||||||
|  |                ws_protocol_lacrosse_tx_const.te_delta * 2) { | ||||||
|  |                 instance->decoder.te_last = duration; | ||||||
|  |                 instance->header_count++; | ||||||
|  |             } else { | ||||||
|  |                 instance->decoder.parser_step = LaCrosse_TXDecoderStepReset; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         break; | ||||||
|  | 
 | ||||||
|  |     case LaCrosse_TXDecoderStepSaveDuration: | ||||||
|  |         if(level) { | ||||||
|  |             instance->decoder.te_last = duration; | ||||||
|  |             instance->decoder.parser_step = LaCrosse_TXDecoderStepCheckDuration; | ||||||
|  |         } else { | ||||||
|  |             instance->decoder.parser_step = LaCrosse_TXDecoderStepReset; | ||||||
|  |         } | ||||||
|  |         break; | ||||||
|  | 
 | ||||||
|  |     case LaCrosse_TXDecoderStepCheckDuration: | ||||||
|  | 
 | ||||||
|  |         if(!level) { | ||||||
|  |             if(duration > LACROSSE_TX_GAP * 3) { | ||||||
|  |                 if(DURATION_DIFF( | ||||||
|  |                        instance->decoder.te_last, ws_protocol_lacrosse_tx_const.te_short) < | ||||||
|  |                    ws_protocol_lacrosse_tx_const.te_delta) { | ||||||
|  |                     subghz_protocol_blocks_add_bit(&instance->decoder, 1); | ||||||
|  |                     instance->decoder.parser_step = LaCrosse_TXDecoderStepSaveDuration; | ||||||
|  |                 } else if( | ||||||
|  |                     DURATION_DIFF( | ||||||
|  |                         instance->decoder.te_last, ws_protocol_lacrosse_tx_const.te_long) < | ||||||
|  |                     ws_protocol_lacrosse_tx_const.te_delta) { | ||||||
|  |                     subghz_protocol_blocks_add_bit(&instance->decoder, 0); | ||||||
|  |                     instance->decoder.parser_step = LaCrosse_TXDecoderStepSaveDuration; | ||||||
|  |                 } | ||||||
|  |                 if((instance->decoder.decode_data & LACROSSE_TX_SUNC_MASK) == | ||||||
|  |                    LACROSSE_TX_SUNC_PATTERN) { | ||||||
|  |                     if(ws_protocol_lacrosse_tx_check_crc(instance)) { | ||||||
|  |                         instance->generic.data = instance->decoder.decode_data; | ||||||
|  |                         instance->generic.data_count_bit = LACROSSE_TX_BIT_SIZE; | ||||||
|  |                         ws_protocol_lacrosse_tx_remote_controller(&instance->generic); | ||||||
|  |                         if(instance->base.callback) | ||||||
|  |                             instance->base.callback(&instance->base, instance->base.context); | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  | 
 | ||||||
|  |                 instance->decoder.decode_data = 0; | ||||||
|  |                 instance->decoder.decode_count_bit = 0; | ||||||
|  |                 instance->header_count = 0; | ||||||
|  |                 instance->decoder.parser_step = LaCrosse_TXDecoderStepReset; | ||||||
|  |                 break; | ||||||
|  |             } else if( | ||||||
|  |                 (DURATION_DIFF(instance->decoder.te_last, ws_protocol_lacrosse_tx_const.te_short) < | ||||||
|  |                  ws_protocol_lacrosse_tx_const.te_delta) && | ||||||
|  |                 (DURATION_DIFF(duration, LACROSSE_TX_GAP) < | ||||||
|  |                  ws_protocol_lacrosse_tx_const.te_delta * 2)) { | ||||||
|  |                 subghz_protocol_blocks_add_bit(&instance->decoder, 1); | ||||||
|  |                 instance->decoder.parser_step = LaCrosse_TXDecoderStepSaveDuration; | ||||||
|  |             } else if( | ||||||
|  |                 (DURATION_DIFF(instance->decoder.te_last, ws_protocol_lacrosse_tx_const.te_long) < | ||||||
|  |                  ws_protocol_lacrosse_tx_const.te_delta) && | ||||||
|  |                 (DURATION_DIFF(duration, LACROSSE_TX_GAP) < | ||||||
|  |                  ws_protocol_lacrosse_tx_const.te_delta * 2)) { | ||||||
|  |                 subghz_protocol_blocks_add_bit(&instance->decoder, 0); | ||||||
|  |                 instance->decoder.parser_step = LaCrosse_TXDecoderStepSaveDuration; | ||||||
|  |             } else { | ||||||
|  |                 instance->decoder.parser_step = LaCrosse_TXDecoderStepReset; | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |         } else { | ||||||
|  |             instance->decoder.parser_step = LaCrosse_TXDecoderStepReset; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         break; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | uint8_t ws_protocol_decoder_lacrosse_tx_get_hash_data(void* context) { | ||||||
|  |     furi_assert(context); | ||||||
|  |     WSProtocolDecoderLaCrosse_TX* instance = context; | ||||||
|  |     return subghz_protocol_blocks_get_hash_data( | ||||||
|  |         &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool ws_protocol_decoder_lacrosse_tx_serialize( | ||||||
|  |     void* context, | ||||||
|  |     FlipperFormat* flipper_format, | ||||||
|  |     SubGhzRadioPreset* preset) { | ||||||
|  |     furi_assert(context); | ||||||
|  |     WSProtocolDecoderLaCrosse_TX* instance = context; | ||||||
|  |     return ws_block_generic_serialize(&instance->generic, flipper_format, preset); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool ws_protocol_decoder_lacrosse_tx_deserialize(void* context, FlipperFormat* flipper_format) { | ||||||
|  |     furi_assert(context); | ||||||
|  |     WSProtocolDecoderLaCrosse_TX* instance = context; | ||||||
|  |     bool ret = false; | ||||||
|  |     do { | ||||||
|  |         if(!ws_block_generic_deserialize(&instance->generic, flipper_format)) { | ||||||
|  |             break; | ||||||
|  |         } | ||||||
|  |         if(instance->generic.data_count_bit != | ||||||
|  |            ws_protocol_lacrosse_tx_const.min_count_bit_for_found) { | ||||||
|  |             FURI_LOG_E(TAG, "Wrong number of bits in key"); | ||||||
|  |             break; | ||||||
|  |         } | ||||||
|  |         ret = true; | ||||||
|  |     } while(false); | ||||||
|  |     return ret; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void ws_protocol_decoder_lacrosse_tx_get_string(void* context, FuriString* output) { | ||||||
|  |     furi_assert(context); | ||||||
|  |     WSProtocolDecoderLaCrosse_TX* instance = context; | ||||||
|  |     furi_string_printf( | ||||||
|  |         output, | ||||||
|  |         "%s %dbit\r\n" | ||||||
|  |         "Key:0x%lX%08lX\r\n" | ||||||
|  |         "Sn:0x%lX Ch:%d  Bat:%d\r\n" | ||||||
|  |         "Temp:%3.1f C Hum:%d%%", | ||||||
|  |         instance->generic.protocol_name, | ||||||
|  |         instance->generic.data_count_bit, | ||||||
|  |         (uint32_t)(instance->generic.data >> 32), | ||||||
|  |         (uint32_t)(instance->generic.data), | ||||||
|  |         instance->generic.id, | ||||||
|  |         instance->generic.channel, | ||||||
|  |         instance->generic.battery_low, | ||||||
|  |         (double)instance->generic.temp, | ||||||
|  |         instance->generic.humidity); | ||||||
|  | } | ||||||
							
								
								
									
										79
									
								
								applications/plugins/weather_station/protocols/lacrosse_tx.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @ -0,0 +1,79 @@ | |||||||
|  | #pragma once | ||||||
|  | 
 | ||||||
|  | #include <lib/subghz/protocols/base.h> | ||||||
|  | 
 | ||||||
|  | #include <lib/subghz/blocks/const.h> | ||||||
|  | #include <lib/subghz/blocks/decoder.h> | ||||||
|  | #include <lib/subghz/blocks/encoder.h> | ||||||
|  | #include "ws_generic.h" | ||||||
|  | #include <lib/subghz/blocks/math.h> | ||||||
|  | 
 | ||||||
|  | #define WS_PROTOCOL_LACROSSE_TX_NAME "LaCrosse_TX" | ||||||
|  | 
 | ||||||
|  | typedef struct WSProtocolDecoderLaCrosse_TX WSProtocolDecoderLaCrosse_TX; | ||||||
|  | typedef struct WSProtocolEncoderLaCrosse_TX WSProtocolEncoderLaCrosse_TX; | ||||||
|  | 
 | ||||||
|  | extern const SubGhzProtocolDecoder ws_protocol_lacrosse_tx_decoder; | ||||||
|  | extern const SubGhzProtocolEncoder ws_protocol_lacrosse_tx_encoder; | ||||||
|  | extern const SubGhzProtocol ws_protocol_lacrosse_tx; | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Allocate WSProtocolDecoderLaCrosse_TX. | ||||||
|  |  * @param environment Pointer to a SubGhzEnvironment instance | ||||||
|  |  * @return WSProtocolDecoderLaCrosse_TX* pointer to a WSProtocolDecoderLaCrosse_TX instance | ||||||
|  |  */ | ||||||
|  | void* ws_protocol_decoder_lacrosse_tx_alloc(SubGhzEnvironment* environment); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Free WSProtocolDecoderLaCrosse_TX. | ||||||
|  |  * @param context Pointer to a WSProtocolDecoderLaCrosse_TX instance | ||||||
|  |  */ | ||||||
|  | void ws_protocol_decoder_lacrosse_tx_free(void* context); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Reset decoder WSProtocolDecoderLaCrosse_TX. | ||||||
|  |  * @param context Pointer to a WSProtocolDecoderLaCrosse_TX instance | ||||||
|  |  */ | ||||||
|  | void ws_protocol_decoder_lacrosse_tx_reset(void* context); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Parse a raw sequence of levels and durations received from the air. | ||||||
|  |  * @param context Pointer to a WSProtocolDecoderLaCrosse_TX instance | ||||||
|  |  * @param level Signal level true-high false-low | ||||||
|  |  * @param duration Duration of this level in, us | ||||||
|  |  */ | ||||||
|  | void ws_protocol_decoder_lacrosse_tx_feed(void* context, bool level, uint32_t duration); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Getting the hash sum of the last randomly received parcel. | ||||||
|  |  * @param context Pointer to a WSProtocolDecoderLaCrosse_TX instance | ||||||
|  |  * @return hash Hash sum | ||||||
|  |  */ | ||||||
|  | uint8_t ws_protocol_decoder_lacrosse_tx_get_hash_data(void* context); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Serialize data WSProtocolDecoderLaCrosse_TX. | ||||||
|  |  * @param context Pointer to a WSProtocolDecoderLaCrosse_TX instance | ||||||
|  |  * @param flipper_format Pointer to a FlipperFormat instance | ||||||
|  |  * @param preset The modulation on which the signal was received, SubGhzRadioPreset | ||||||
|  |  * @return true On success | ||||||
|  |  */ | ||||||
|  | bool ws_protocol_decoder_lacrosse_tx_serialize( | ||||||
|  |     void* context, | ||||||
|  |     FlipperFormat* flipper_format, | ||||||
|  |     SubGhzRadioPreset* preset); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Deserialize data WSProtocolDecoderLaCrosse_TX. | ||||||
|  |  * @param context Pointer to a WSProtocolDecoderLaCrosse_TX instance | ||||||
|  |  * @param flipper_format Pointer to a FlipperFormat instance | ||||||
|  |  * @return true On success | ||||||
|  |  */ | ||||||
|  | bool ws_protocol_decoder_lacrosse_tx_deserialize(void* context, FlipperFormat* flipper_format); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Getting a textual representation of the received data. | ||||||
|  |  * @param context Pointer to a WSProtocolDecoderLaCrosse_TX instance | ||||||
|  |  * @param output Resulting text | ||||||
|  |  */ | ||||||
|  | void ws_protocol_decoder_lacrosse_tx_get_string(void* context, FuriString* output); | ||||||
| @ -8,6 +8,7 @@ const SubGhzProtocol* weather_station_protocol_registry_items[] = { | |||||||
|     &ws_protocol_gt_wt_03, |     &ws_protocol_gt_wt_03, | ||||||
|     &ws_protocol_acurite_606tx, |     &ws_protocol_acurite_606tx, | ||||||
|     &ws_protocol_acurite_609txc, |     &ws_protocol_acurite_609txc, | ||||||
|  |     &ws_protocol_lacrosse_tx, | ||||||
|     &ws_protocol_lacrosse_tx141thbv2, |     &ws_protocol_lacrosse_tx141thbv2, | ||||||
|     &ws_protocol_oregon2, |     &ws_protocol_oregon2, | ||||||
|     &ws_protocol_acurite_592txr, |     &ws_protocol_acurite_592txr, | ||||||
|  | |||||||
| @ -8,6 +8,7 @@ | |||||||
| #include "gt_wt_03.h" | #include "gt_wt_03.h" | ||||||
| #include "acurite_606tx.h" | #include "acurite_606tx.h" | ||||||
| #include "acurite_609txc.h" | #include "acurite_609txc.h" | ||||||
|  | #include "lacrosse_tx.h" | ||||||
| #include "lacrosse_tx141thbv2.h" | #include "lacrosse_tx141thbv2.h" | ||||||
| #include "oregon2.h" | #include "oregon2.h" | ||||||
| #include "acurite_592txr.h" | #include "acurite_592txr.h" | ||||||
|  | |||||||
| @ -5,6 +5,10 @@ | |||||||
| #include <stdint.h> | #include <stdint.h> | ||||||
| #include <stdbool.h> | #include <stdbool.h> | ||||||
| 
 | 
 | ||||||
|  | #ifdef __cplusplus | ||||||
|  | extern "C" { | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
| typedef struct { | typedef struct { | ||||||
|     bool enabled; |     bool enabled; | ||||||
| } BtSettings; | } BtSettings; | ||||||
| @ -12,3 +16,7 @@ typedef struct { | |||||||
| bool bt_settings_load(BtSettings* bt_settings); | bool bt_settings_load(BtSettings* bt_settings); | ||||||
| 
 | 
 | ||||||
| bool bt_settings_save(BtSettings* bt_settings); | bool bt_settings_save(BtSettings* bt_settings); | ||||||
|  | 
 | ||||||
|  | #ifdef __cplusplus | ||||||
|  | } | ||||||
|  | #endif | ||||||
| @ -56,7 +56,7 @@ static bool desktop_view_slideshow_input(InputEvent* event, void* context) { | |||||||
|             instance->callback(DesktopSlideshowCompleted, instance->context); |             instance->callback(DesktopSlideshowCompleted, instance->context); | ||||||
|         } |         } | ||||||
|         update_view = true; |         update_view = true; | ||||||
|     } else if(event->key == InputKeyOk) { |     } else if(event->key == InputKeyOk && instance->timer) { | ||||||
|         if(event->type == InputTypePress) { |         if(event->type == InputTypePress) { | ||||||
|             furi_timer_start(instance->timer, DESKTOP_SLIDESHOW_POWEROFF_SHORT); |             furi_timer_start(instance->timer, DESKTOP_SLIDESHOW_POWEROFF_SHORT); | ||||||
|         } else if(event->type == InputTypeRelease) { |         } else if(event->type == InputTypeRelease) { | ||||||
|  | |||||||
| @ -178,6 +178,47 @@ static void button_menu_process_down(ButtonMenu* button_menu) { | |||||||
|         true); |         true); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | static void button_menu_process_right(ButtonMenu* button_menu) { | ||||||
|  |     furi_assert(button_menu); | ||||||
|  | 
 | ||||||
|  |     with_view_model( | ||||||
|  |         button_menu->view, | ||||||
|  |         ButtonMenuModel * model, | ||||||
|  |         { | ||||||
|  |             if(ButtonMenuItemArray_size(model->items) > BUTTONS_PER_SCREEN) { | ||||||
|  |                 size_t position_candidate = model->position + BUTTONS_PER_SCREEN; | ||||||
|  |                 position_candidate -= position_candidate % BUTTONS_PER_SCREEN; | ||||||
|  |                 if(position_candidate < (ButtonMenuItemArray_size(model->items))) { | ||||||
|  |                     model->position = position_candidate; | ||||||
|  |                 } else { | ||||||
|  |                     model->position = 0; | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |         }, | ||||||
|  |         true); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void button_menu_process_left(ButtonMenu* button_menu) { | ||||||
|  |     furi_assert(button_menu); | ||||||
|  | 
 | ||||||
|  |     with_view_model( | ||||||
|  |         button_menu->view, | ||||||
|  |         ButtonMenuModel * model, | ||||||
|  |         { | ||||||
|  |             if(ButtonMenuItemArray_size(model->items) > BUTTONS_PER_SCREEN) { | ||||||
|  |                 size_t position_candidate; | ||||||
|  |                 if(model->position < BUTTONS_PER_SCREEN) { | ||||||
|  |                     position_candidate = (ButtonMenuItemArray_size(model->items) - 1); | ||||||
|  |                 } else { | ||||||
|  |                     position_candidate = model->position - BUTTONS_PER_SCREEN; | ||||||
|  |                 }; | ||||||
|  |                 position_candidate -= position_candidate % BUTTONS_PER_SCREEN; | ||||||
|  |                 model->position = position_candidate; | ||||||
|  |             } | ||||||
|  |         }, | ||||||
|  |         true); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| static void button_menu_process_ok(ButtonMenu* button_menu, InputType type) { | static void button_menu_process_ok(ButtonMenu* button_menu, InputType type) { | ||||||
|     furi_assert(button_menu); |     furi_assert(button_menu); | ||||||
| 
 | 
 | ||||||
| @ -239,6 +280,14 @@ static bool button_menu_view_input_callback(InputEvent* event, void* context) { | |||||||
|             consumed = true; |             consumed = true; | ||||||
|             button_menu_process_down(button_menu); |             button_menu_process_down(button_menu); | ||||||
|             break; |             break; | ||||||
|  |         case InputKeyRight: | ||||||
|  |             consumed = true; | ||||||
|  |             button_menu_process_right(button_menu); | ||||||
|  |             break; | ||||||
|  |         case InputKeyLeft: | ||||||
|  |             consumed = true; | ||||||
|  |             button_menu_process_left(button_menu); | ||||||
|  |             break; | ||||||
|         default: |         default: | ||||||
|             break; |             break; | ||||||
|         } |         } | ||||||
|  | |||||||
| @ -29,7 +29,7 @@ typedef struct { | |||||||
|     TextInputValidatorCallback validator_callback; |     TextInputValidatorCallback validator_callback; | ||||||
|     void* validator_callback_context; |     void* validator_callback_context; | ||||||
|     FuriString* validator_text; |     FuriString* validator_text; | ||||||
|     bool valadator_message_visible; |     bool validator_message_visible; | ||||||
| } TextInputModel; | } TextInputModel; | ||||||
| 
 | 
 | ||||||
| static const uint8_t keyboard_origin_x = 1; | static const uint8_t keyboard_origin_x = 1; | ||||||
| @ -138,7 +138,7 @@ static bool char_is_lowercase(char letter) { | |||||||
| static char char_to_uppercase(const char letter) { | static char char_to_uppercase(const char letter) { | ||||||
|     if(letter == '_') { |     if(letter == '_') { | ||||||
|         return 0x20; |         return 0x20; | ||||||
|     } else if(isalpha(letter)) { |     } else if(islower(letter)) { | ||||||
|         return (letter - 0x20); |         return (letter - 0x20); | ||||||
|     } else { |     } else { | ||||||
|         return letter; |         return letter; | ||||||
| @ -254,7 +254,7 @@ static void text_input_view_draw_callback(Canvas* canvas, void* _model) { | |||||||
|             } |             } | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|     if(model->valadator_message_visible) { |     if(model->validator_message_visible) { | ||||||
|         canvas_set_font(canvas, FontSecondary); |         canvas_set_font(canvas, FontSecondary); | ||||||
|         canvas_set_color(canvas, ColorWhite); |         canvas_set_color(canvas, ColorWhite); | ||||||
|         canvas_draw_box(canvas, 8, 10, 110, 48); |         canvas_draw_box(canvas, 8, 10, 110, 48); | ||||||
| @ -309,7 +309,9 @@ static void text_input_handle_ok(TextInput* text_input, TextInputModel* model, b | |||||||
|     char selected = get_selected_char(model); |     char selected = get_selected_char(model); | ||||||
|     size_t text_length = strlen(model->text_buffer); |     size_t text_length = strlen(model->text_buffer); | ||||||
| 
 | 
 | ||||||
|     if(shift) { |     bool toogle_case = text_length == 0; | ||||||
|  |     if(shift) toogle_case = !toogle_case; | ||||||
|  |     if(toogle_case) { | ||||||
|         selected = char_to_uppercase(selected); |         selected = char_to_uppercase(selected); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
| @ -317,7 +319,7 @@ static void text_input_handle_ok(TextInput* text_input, TextInputModel* model, b | |||||||
|         if(model->validator_callback && |         if(model->validator_callback && | ||||||
|            (!model->validator_callback( |            (!model->validator_callback( | ||||||
|                model->text_buffer, model->validator_text, model->validator_callback_context))) { |                model->text_buffer, model->validator_text, model->validator_callback_context))) { | ||||||
|             model->valadator_message_visible = true; |             model->validator_message_visible = true; | ||||||
|             furi_timer_start(text_input->timer, furi_kernel_get_tick_frequency() * 4); |             furi_timer_start(text_input->timer, furi_kernel_get_tick_frequency() * 4); | ||||||
|         } else if(model->callback != 0 && text_length > 0) { |         } else if(model->callback != 0 && text_length > 0) { | ||||||
|             model->callback(model->callback_context); |             model->callback(model->callback_context); | ||||||
| @ -329,9 +331,6 @@ static void text_input_handle_ok(TextInput* text_input, TextInputModel* model, b | |||||||
|             text_length = 0; |             text_length = 0; | ||||||
|         } |         } | ||||||
|         if(text_length < (model->text_buffer_size - 1)) { |         if(text_length < (model->text_buffer_size - 1)) { | ||||||
|             if(text_length == 0 && char_is_lowercase(selected)) { |  | ||||||
|                 selected = char_to_uppercase(selected); |  | ||||||
|             } |  | ||||||
|             model->text_buffer[text_length] = selected; |             model->text_buffer[text_length] = selected; | ||||||
|             model->text_buffer[text_length + 1] = 0; |             model->text_buffer[text_length + 1] = 0; | ||||||
|         } |         } | ||||||
| @ -349,8 +348,8 @@ static bool text_input_view_input_callback(InputEvent* event, void* context) { | |||||||
|     TextInputModel* model = view_get_model(text_input->view); |     TextInputModel* model = view_get_model(text_input->view); | ||||||
| 
 | 
 | ||||||
|     if((!(event->type == InputTypePress) && !(event->type == InputTypeRelease)) && |     if((!(event->type == InputTypePress) && !(event->type == InputTypeRelease)) && | ||||||
|        model->valadator_message_visible) { |        model->validator_message_visible) { | ||||||
|         model->valadator_message_visible = false; |         model->validator_message_visible = false; | ||||||
|         consumed = true; |         consumed = true; | ||||||
|     } else if(event->type == InputTypeShort) { |     } else if(event->type == InputTypeShort) { | ||||||
|         consumed = true; |         consumed = true; | ||||||
| @ -436,7 +435,7 @@ void text_input_timer_callback(void* context) { | |||||||
|     with_view_model( |     with_view_model( | ||||||
|         text_input->view, |         text_input->view, | ||||||
|         TextInputModel * model, |         TextInputModel * model, | ||||||
|         { model->valadator_message_visible = false; }, |         { model->validator_message_visible = false; }, | ||||||
|         true); |         true); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| @ -496,7 +495,7 @@ void text_input_reset(TextInput* text_input) { | |||||||
|             model->validator_callback = NULL; |             model->validator_callback = NULL; | ||||||
|             model->validator_callback_context = NULL; |             model->validator_callback_context = NULL; | ||||||
|             furi_string_reset(model->validator_text); |             furi_string_reset(model->validator_text); | ||||||
|             model->valadator_message_visible = false; |             model->validator_message_visible = false; | ||||||
|         }, |         }, | ||||||
|         true); |         true); | ||||||
| } | } | ||||||
|  | |||||||
| @ -91,7 +91,7 @@ void widget_add_string_element( | |||||||
|  * @param[in]  text             Formatted text. The following formats are available: |  * @param[in]  text             Formatted text. The following formats are available: | ||||||
|  *                               "\e#Bold text\e#" - bold font is used |  *                               "\e#Bold text\e#" - bold font is used | ||||||
|  *                               "\e*Monospaced text\e*" - monospaced font is used |  *                               "\e*Monospaced text\e*" - monospaced font is used | ||||||
|  *                               "\e#Inversed text\e#" - white text on black background |  *                               "\e!Inversed text\e!" - white text on black background | ||||||
|  * @param      strip_to_dots    Strip text to ... if does not fit to width |  * @param      strip_to_dots    Strip text to ... if does not fit to width | ||||||
|  */ |  */ | ||||||
| void widget_add_text_box_element( | void widget_add_text_box_element( | ||||||
|  | |||||||
| @ -19,7 +19,7 @@ extern "C" { | |||||||
| typedef enum { | typedef enum { | ||||||
|     InputTypePress, /**< Press event, emitted after debounce */ |     InputTypePress, /**< Press event, emitted after debounce */ | ||||||
|     InputTypeRelease, /**< Release event, emitted after debounce */ |     InputTypeRelease, /**< Release event, emitted after debounce */ | ||||||
|     InputTypeShort, /**< Short event, emitted after InputTypeRelease done withing INPUT_LONG_PRESS interval */ |     InputTypeShort, /**< Short event, emitted after InputTypeRelease done within INPUT_LONG_PRESS interval */ | ||||||
|     InputTypeLong, /**< Long event, emitted after INPUT_LONG_PRESS_COUNTS interval, asynchronous to InputTypeRelease  */ |     InputTypeLong, /**< Long event, emitted after INPUT_LONG_PRESS_COUNTS interval, asynchronous to InputTypeRelease  */ | ||||||
|     InputTypeRepeat, /**< Repeat event, emitted with INPUT_LONG_PRESS_COUNTS period after InputTypeLong event */ |     InputTypeRepeat, /**< Repeat event, emitted with INPUT_LONG_PRESS_COUNTS period after InputTypeLong event */ | ||||||
|     InputTypeMAX, /**< Special value for exceptional */ |     InputTypeMAX, /**< Special value for exceptional */ | ||||||
|  | |||||||
| @ -1,5 +1,6 @@ | |||||||
| #include <furi.h> | #include <furi.h> | ||||||
| #include <furi_hal.h> | #include <furi_hal.h> | ||||||
|  | #include <stm32_adafruit_sd.h> | ||||||
| 
 | 
 | ||||||
| #include <cli/cli.h> | #include <cli/cli.h> | ||||||
| #include <lib/toolbox/args.h> | #include <lib/toolbox/args.h> | ||||||
| @ -60,17 +61,28 @@ static void storage_cli_info(Cli* cli, FuriString* path) { | |||||||
|         } |         } | ||||||
|     } else if(furi_string_cmp_str(path, STORAGE_EXT_PATH_PREFIX) == 0) { |     } else if(furi_string_cmp_str(path, STORAGE_EXT_PATH_PREFIX) == 0) { | ||||||
|         SDInfo sd_info; |         SDInfo sd_info; | ||||||
|  |         SD_CID sd_cid; | ||||||
|         FS_Error error = storage_sd_info(api, &sd_info); |         FS_Error error = storage_sd_info(api, &sd_info); | ||||||
|  |         BSP_SD_GetCIDRegister(&sd_cid); | ||||||
| 
 | 
 | ||||||
|         if(error != FSE_OK) { |         if(error != FSE_OK) { | ||||||
|             storage_cli_print_error(error); |             storage_cli_print_error(error); | ||||||
|         } else { |         } else { | ||||||
|             printf( |             printf( | ||||||
|                 "Label: %s\r\nType: %s\r\n%luKiB total\r\n%luKiB free\r\n", |                 "Label: %s\r\nType: %s\r\n%luKiB total\r\n%luKiB free\r\n" | ||||||
|  |                 "%02x%2.2s %5.5s %i.%i\r\nSN:%04lx %02i/%i\r\n", | ||||||
|                 sd_info.label, |                 sd_info.label, | ||||||
|                 sd_api_get_fs_type_text(sd_info.fs_type), |                 sd_api_get_fs_type_text(sd_info.fs_type), | ||||||
|                 sd_info.kb_total, |                 sd_info.kb_total, | ||||||
|                 sd_info.kb_free); |                 sd_info.kb_free, | ||||||
|  |                 sd_cid.ManufacturerID, | ||||||
|  |                 sd_cid.OEM_AppliID, | ||||||
|  |                 sd_cid.ProdName, | ||||||
|  |                 sd_cid.ProdRev >> 4, | ||||||
|  |                 sd_cid.ProdRev & 0xf, | ||||||
|  |                 sd_cid.ProdSN, | ||||||
|  |                 sd_cid.ManufactMonth, | ||||||
|  |                 sd_cid.ManufactYear + 2000); | ||||||
|         } |         } | ||||||
|     } else { |     } else { | ||||||
|         storage_cli_print_usage(); |         storage_cli_print_usage(); | ||||||
|  | |||||||
| @ -1,4 +1,5 @@ | |||||||
| #include "../storage_settings.h" | #include "../storage_settings.h" | ||||||
|  | #include <stm32_adafruit_sd.h> | ||||||
| 
 | 
 | ||||||
| static void storage_settings_scene_sd_info_dialog_callback(DialogExResult result, void* context) { | static void storage_settings_scene_sd_info_dialog_callback(DialogExResult result, void* context) { | ||||||
|     StorageSettings* app = context; |     StorageSettings* app = context; | ||||||
| @ -11,7 +12,10 @@ void storage_settings_scene_sd_info_on_enter(void* context) { | |||||||
|     DialogEx* dialog_ex = app->dialog_ex; |     DialogEx* dialog_ex = app->dialog_ex; | ||||||
| 
 | 
 | ||||||
|     SDInfo sd_info; |     SDInfo sd_info; | ||||||
|  |     SD_CID sd_cid; | ||||||
|     FS_Error sd_status = storage_sd_info(app->fs_api, &sd_info); |     FS_Error sd_status = storage_sd_info(app->fs_api, &sd_info); | ||||||
|  |     BSP_SD_GetCIDRegister(&sd_cid); | ||||||
|  | 
 | ||||||
|     scene_manager_set_scene_state(app->scene_manager, StorageSettingsSDInfo, sd_status); |     scene_manager_set_scene_state(app->scene_manager, StorageSettingsSDInfo, sd_status); | ||||||
| 
 | 
 | ||||||
|     dialog_ex_set_context(dialog_ex, app); |     dialog_ex_set_context(dialog_ex, app); | ||||||
| @ -26,13 +30,22 @@ void storage_settings_scene_sd_info_on_enter(void* context) { | |||||||
|     } else { |     } else { | ||||||
|         furi_string_printf( |         furi_string_printf( | ||||||
|             app->text_string, |             app->text_string, | ||||||
|             "Label: %s\nType: %s\n%lu KiB total\n%lu KiB free", |             "Label: %s\nType: %s\n%lu KiB total\n%lu KiB free\n" | ||||||
|  |             "%02X%2.2s %5.5s %i.%i\nSN:%04lX %02i/%i", | ||||||
|             sd_info.label, |             sd_info.label, | ||||||
|             sd_api_get_fs_type_text(sd_info.fs_type), |             sd_api_get_fs_type_text(sd_info.fs_type), | ||||||
|             sd_info.kb_total, |             sd_info.kb_total, | ||||||
|             sd_info.kb_free); |             sd_info.kb_free, | ||||||
|  |             sd_cid.ManufacturerID, | ||||||
|  |             sd_cid.OEM_AppliID, | ||||||
|  |             sd_cid.ProdName, | ||||||
|  |             sd_cid.ProdRev >> 4, | ||||||
|  |             sd_cid.ProdRev & 0xf, | ||||||
|  |             sd_cid.ProdSN, | ||||||
|  |             sd_cid.ManufactMonth, | ||||||
|  |             sd_cid.ManufactYear + 2000); | ||||||
|         dialog_ex_set_text( |         dialog_ex_set_text( | ||||||
|             dialog_ex, furi_string_get_cstr(app->text_string), 4, 4, AlignLeft, AlignTop); |             dialog_ex, furi_string_get_cstr(app->text_string), 4, 1, AlignLeft, AlignTop); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     view_dispatcher_switch_to_view(app->view_dispatcher, StorageSettingsViewDialogEx); |     view_dispatcher_switch_to_view(app->view_dispatcher, StorageSettingsViewDialogEx); | ||||||
|  | |||||||
| @ -54,13 +54,14 @@ assetsenv.Alias("proto_ver", proto_ver) | |||||||
| 
 | 
 | ||||||
| # Gather everything into a static lib | # Gather everything into a static lib | ||||||
| assets_parts = (icons, proto, dolphin_blocking, dolphin_internal, proto_ver) | assets_parts = (icons, proto, dolphin_blocking, dolphin_internal, proto_ver) | ||||||
|  | env.Replace(FW_ASSETS_HEADERS=assets_parts) | ||||||
| 
 | 
 | ||||||
| assetslib = assetsenv.Library("${FW_LIB_NAME}", assets_parts) | assetslib = assetsenv.Library("${FW_LIB_NAME}", assets_parts) | ||||||
| assetsenv.Install("${LIB_DIST_DIR}", assetslib) | assetsenv.Install("${LIB_DIST_DIR}", assetslib) | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| # Resources for SD card | # Resources for SD card | ||||||
| 
 | env.SetDefault(FW_RESOURCES=None) | ||||||
| if assetsenv["IS_BASE_FIRMWARE"]: | if assetsenv["IS_BASE_FIRMWARE"]: | ||||||
|     # External dolphin animations |     # External dolphin animations | ||||||
|     dolphin_external = assetsenv.DolphinExtBuilder( |     dolphin_external = assetsenv.DolphinExtBuilder( | ||||||
| @ -92,8 +93,7 @@ if assetsenv["IS_BASE_FIRMWARE"]: | |||||||
|     ) |     ) | ||||||
| 
 | 
 | ||||||
|     # Exporting resources node to external environment |     # Exporting resources node to external environment | ||||||
|     env["FW_ASSETS_HEADERS"] = assets_parts |     env.Replace(FW_RESOURCES=resources) | ||||||
|     env["FW_RESOURCES"] = resources |  | ||||||
|     assetsenv.Alias("resources", resources) |     assetsenv.Alias("resources", resources) | ||||||
| 
 | 
 | ||||||
| Return("assetslib") | Return("assetslib") | ||||||
|  | |||||||
| Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 1.4 KiB | 
| Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 1.4 KiB | 
| Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 1.4 KiB | 
| Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 1.4 KiB | 
| Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 1.3 KiB | 
| Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 1.4 KiB | 
| Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 1.4 KiB | 
| Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 1.4 KiB | 
| Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 1.4 KiB | 
| Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 1.4 KiB | 
| Before Width: | Height: | Size: 1.3 KiB After Width: | Height: | Size: 1.3 KiB | 
| Before Width: | Height: | Size: 1.9 KiB After Width: | Height: | Size: 2.0 KiB | 
| Before Width: | Height: | Size: 1.9 KiB After Width: | Height: | Size: 2.0 KiB | 
| Before Width: | Height: | Size: 1.9 KiB After Width: | Height: | Size: 2.0 KiB | 
| Before Width: | Height: | Size: 1.3 KiB After Width: | Height: | Size: 1.4 KiB | 
| Before Width: | Height: | Size: 1.3 KiB After Width: | Height: | Size: 1.4 KiB | 
| Before Width: | Height: | Size: 1.3 KiB After Width: | Height: | Size: 1.4 KiB | 
| Before Width: | Height: | Size: 1.3 KiB After Width: | Height: | Size: 1.4 KiB | 
| Before Width: | Height: | Size: 1.3 KiB After Width: | Height: | Size: 1.4 KiB | 
| Before Width: | Height: | Size: 1.3 KiB After Width: | Height: | Size: 1.4 KiB | 
| @ -79,7 +79,7 @@ STRING Flipper Zero BadUSB feature is compatible with USB Rubber Ducky script fo | |||||||
| ENTER | ENTER | ||||||
| STRING More information about script syntax can be found here: | STRING More information about script syntax can be found here: | ||||||
| ENTER | ENTER | ||||||
| STRING https://github.com/hak5darren/USB-Rubber-Ducky/wiki/Duckyscript | STRING https://github.com/flipperdevices/flipperzero-firmware/blob/dev/documentation/file_formats/BadUsbScriptFormat.md | ||||||
| ENTER | ENTER | ||||||
| 
 | 
 | ||||||
| STRING EOF | STRING EOF | ||||||
|  | |||||||
| @ -80,5 +80,5 @@ STRING Flipper Zero BadUSB feature is compatible with USB Rubber Ducky script fo | |||||||
| ENTER | ENTER | ||||||
| STRING More information about script syntax can be found here: | STRING More information about script syntax can be found here: | ||||||
| ENTER | ENTER | ||||||
| STRING https://github.com/hak5darren/USB-Rubber-Ducky/wiki/Duckyscript | STRING https://github.com/flipperdevices/flipperzero-firmware/blob/dev/documentation/file_formats/BadUsbScriptFormat.md | ||||||
| ENTER | ENTER | ||||||
|  | |||||||
							
								
								
									
										814
									
								
								assets/resources/infrared/assets/tv.ir
									
									
									
									
									
										
										
										Executable file → Normal file
									
								
							
							
						
						| @ -56,6 +56,7 @@ To run cleanup (think of `make clean`) for specified targets, add the `-c` optio | |||||||
| - `get_stlink` - output serial numbers for attached STLink probes. Used for specifying an adapter with `OPENOCD_ADAPTER_SERIAL=...`. | - `get_stlink` - output serial numbers for attached STLink probes. Used for specifying an adapter with `OPENOCD_ADAPTER_SERIAL=...`. | ||||||
| - `lint`, `format` - run clang-format on the C source code to check and reformat it according to the `.clang-format` specs. | - `lint`, `format` - run clang-format on the C source code to check and reformat it according to the `.clang-format` specs. | ||||||
| - `lint_py`, `format_py` - run [black](https://black.readthedocs.io/en/stable/index.html) on the Python source code, build system files & application manifests. | - `lint_py`, `format_py` - run [black](https://black.readthedocs.io/en/stable/index.html) on the Python source code, build system files & application manifests. | ||||||
|  | - `firmware_pvs` - generate a PVS Studio report for the firmware. Requires PVS Studio to be availabe on your system's `PATH`. | ||||||
| - `cli` - start a Flipper CLI session over USB. | - `cli` - start a Flipper CLI session over USB. | ||||||
| 
 | 
 | ||||||
| ### Firmware targets | ### Firmware targets | ||||||
|  | |||||||
| @ -67,6 +67,7 @@ Can be combined with a special key command or a single character. | |||||||
| |ALT-SHIFT|ALT+SHIFT| | |ALT-SHIFT|ALT+SHIFT| | ||||||
| |ALT-GUI|ALT+WIN| | |ALT-GUI|ALT+WIN| | ||||||
| |GUI-SHIFT|WIN+SHIFT| | |GUI-SHIFT|WIN+SHIFT| | ||||||
|  | |GUI-CTRL|WIN+CTRL| | ||||||
| 
 | 
 | ||||||
| ## String | ## String | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -15,6 +15,7 @@ env = ENV.Clone( | |||||||
|         ("compilation_db", {"COMPILATIONDB_COMSTR": "\tCDB\t${TARGET}"}), |         ("compilation_db", {"COMPILATIONDB_COMSTR": "\tCDB\t${TARGET}"}), | ||||||
|         "fwbin", |         "fwbin", | ||||||
|         "fbt_apps", |         "fbt_apps", | ||||||
|  |         "pvsstudio", | ||||||
|     ], |     ], | ||||||
|     COMPILATIONDB_USE_ABSPATH=False, |     COMPILATIONDB_USE_ABSPATH=False, | ||||||
|     BUILD_DIR=fw_build_meta["build_dir"], |     BUILD_DIR=fw_build_meta["build_dir"], | ||||||
| @ -69,6 +70,8 @@ env = ENV.Clone( | |||||||
|             ], |             ], | ||||||
|         }, |         }, | ||||||
|     }, |     }, | ||||||
|  |     SDK_APISYMS=None, | ||||||
|  |     _APP_ICONS=None, | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| @ -128,9 +131,6 @@ if extra_int_apps := GetOption("extra_int_apps"): | |||||||
|     fwenv.Append(APPS=extra_int_apps.split(",")) |     fwenv.Append(APPS=extra_int_apps.split(",")) | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| if fwenv["FAP_EXAMPLES"]: |  | ||||||
|     fwenv.Append(APPDIRS=[("applications/examples", False)]) |  | ||||||
| 
 |  | ||||||
| for app_dir, _ in env["APPDIRS"]: | for app_dir, _ in env["APPDIRS"]: | ||||||
|     app_dir_node = env.Dir("#").Dir(app_dir) |     app_dir_node = env.Dir("#").Dir(app_dir) | ||||||
| 
 | 
 | ||||||
| @ -273,6 +273,24 @@ Precious(fwcdb) | |||||||
| NoClean(fwcdb) | NoClean(fwcdb) | ||||||
| Alias(fwenv["FIRMWARE_BUILD_CFG"] + "_cdb", fwcdb) | Alias(fwenv["FIRMWARE_BUILD_CFG"] + "_cdb", fwcdb) | ||||||
| 
 | 
 | ||||||
|  | pvscheck = fwenv.PVSCheck("pvsreport.log", fwcdb) | ||||||
|  | Depends( | ||||||
|  |     pvscheck, | ||||||
|  |     [ | ||||||
|  |         fwenv["FW_VERSION_JSON"], | ||||||
|  |         fwenv["FW_ASSETS_HEADERS"], | ||||||
|  |         fwenv["SDK_APISYMS"], | ||||||
|  |         fwenv["_APP_ICONS"], | ||||||
|  |     ], | ||||||
|  | ) | ||||||
|  | Alias(fwenv["FIRMWARE_BUILD_CFG"] + "_pvscheck", pvscheck) | ||||||
|  | AlwaysBuild(pvscheck) | ||||||
|  | Precious(pvscheck) | ||||||
|  | 
 | ||||||
|  | pvsreport = fwenv.PVSReport(None, pvscheck, REPORT_DIR=Dir("pvsreport")) | ||||||
|  | Alias(fwenv["FIRMWARE_BUILD_CFG"] + "_pvs", pvsreport) | ||||||
|  | AlwaysBuild(pvsreport) | ||||||
|  | 
 | ||||||
| # If current configuration was explicitly requested, generate compilation database | # If current configuration was explicitly requested, generate compilation database | ||||||
| # and link its directory as build/latest | # and link its directory as build/latest | ||||||
| if should_gen_cdb_and_link_dir(fwenv, BUILD_TARGETS): | if should_gen_cdb_and_link_dir(fwenv, BUILD_TARGETS): | ||||||
|  | |||||||
| @ -1,5 +1,5 @@ | |||||||
| entry,status,name,type,params | entry,status,name,type,params | ||||||
| Version,+,11.6,, | Version,+,11.9,, | ||||||
| Header,+,applications/services/bt/bt_service/bt.h,, | Header,+,applications/services/bt/bt_service/bt.h,, | ||||||
| Header,+,applications/services/cli/cli.h,, | Header,+,applications/services/cli/cli.h,, | ||||||
| Header,+,applications/services/cli/cli_vcp.h,, | Header,+,applications/services/cli/cli_vcp.h,, | ||||||
| @ -188,6 +188,7 @@ Header,+,lib/toolbox/stream/file_stream.h,, | |||||||
| Header,+,lib/toolbox/stream/stream.h,, | Header,+,lib/toolbox/stream/stream.h,, | ||||||
| Header,+,lib/toolbox/stream/string_stream.h,, | Header,+,lib/toolbox/stream/string_stream.h,, | ||||||
| Header,+,lib/toolbox/tar/tar_archive.h,, | Header,+,lib/toolbox/tar/tar_archive.h,, | ||||||
|  | Header,+,lib/toolbox/value_index.h,, | ||||||
| Header,+,lib/toolbox/version.h,, | Header,+,lib/toolbox/version.h,, | ||||||
| Function,-,LL_ADC_CommonDeInit,ErrorStatus,ADC_Common_TypeDef* | Function,-,LL_ADC_CommonDeInit,ErrorStatus,ADC_Common_TypeDef* | ||||||
| Function,-,LL_ADC_CommonInit,ErrorStatus,"ADC_Common_TypeDef*, LL_ADC_CommonInitTypeDef*" | Function,-,LL_ADC_CommonInit,ErrorStatus,"ADC_Common_TypeDef*, LL_ADC_CommonInitTypeDef*" | ||||||
| @ -1389,6 +1390,7 @@ Function,-,furi_hal_vibro_init,void, | |||||||
| Function,+,furi_hal_vibro_on,void,_Bool | Function,+,furi_hal_vibro_on,void,_Bool | ||||||
| Function,-,furi_init,void, | Function,-,furi_init,void, | ||||||
| Function,+,furi_kernel_get_tick_frequency,uint32_t, | Function,+,furi_kernel_get_tick_frequency,uint32_t, | ||||||
|  | Function,+,furi_kernel_is_irq_or_masked,_Bool, | ||||||
| Function,+,furi_kernel_lock,int32_t, | Function,+,furi_kernel_lock,int32_t, | ||||||
| Function,+,furi_kernel_restore_lock,int32_t,int32_t | Function,+,furi_kernel_restore_lock,int32_t,int32_t | ||||||
| Function,+,furi_kernel_unlock,int32_t, | Function,+,furi_kernel_unlock,int32_t, | ||||||
| @ -1513,6 +1515,7 @@ Function,+,furi_thread_get_name,const char*,FuriThreadId | |||||||
| Function,+,furi_thread_get_return_code,int32_t,FuriThread* | Function,+,furi_thread_get_return_code,int32_t,FuriThread* | ||||||
| Function,+,furi_thread_get_stack_space,uint32_t,FuriThreadId | Function,+,furi_thread_get_stack_space,uint32_t,FuriThreadId | ||||||
| Function,+,furi_thread_get_state,FuriThreadState,FuriThread* | Function,+,furi_thread_get_state,FuriThreadState,FuriThread* | ||||||
|  | Function,+,furi_thread_get_stdout_callback,FuriThreadStdoutWriteCallback, | ||||||
| Function,+,furi_thread_is_suspended,_Bool,FuriThreadId | Function,+,furi_thread_is_suspended,_Bool,FuriThreadId | ||||||
| Function,+,furi_thread_join,_Bool,FuriThread* | Function,+,furi_thread_join,_Bool,FuriThread* | ||||||
| Function,+,furi_thread_mark_as_service,void,FuriThread* | Function,+,furi_thread_mark_as_service,void,FuriThread* | ||||||
| @ -1630,6 +1633,7 @@ Function,+,infrared_get_protocol_by_name,InfraredProtocol,const char* | |||||||
| Function,+,infrared_get_protocol_command_length,uint8_t,InfraredProtocol | Function,+,infrared_get_protocol_command_length,uint8_t,InfraredProtocol | ||||||
| Function,+,infrared_get_protocol_duty_cycle,float,InfraredProtocol | Function,+,infrared_get_protocol_duty_cycle,float,InfraredProtocol | ||||||
| Function,+,infrared_get_protocol_frequency,uint32_t,InfraredProtocol | Function,+,infrared_get_protocol_frequency,uint32_t,InfraredProtocol | ||||||
|  | Function,+,infrared_get_protocol_min_repeat_count,size_t,InfraredProtocol | ||||||
| Function,+,infrared_get_protocol_name,const char*,InfraredProtocol | Function,+,infrared_get_protocol_name,const char*,InfraredProtocol | ||||||
| Function,+,infrared_is_protocol_valid,_Bool,InfraredProtocol | Function,+,infrared_is_protocol_valid,_Bool,InfraredProtocol | ||||||
| Function,+,infrared_reset_decoder,void,InfraredDecoderHandler* | Function,+,infrared_reset_decoder,void,InfraredDecoderHandler* | ||||||
| @ -1879,7 +1883,7 @@ Function,-,mf_classic_dict_is_key_present,_Bool,"MfClassicDict*, uint8_t*" | |||||||
| Function,-,mf_classic_dict_is_key_present_str,_Bool,"MfClassicDict*, FuriString*" | Function,-,mf_classic_dict_is_key_present_str,_Bool,"MfClassicDict*, FuriString*" | ||||||
| Function,-,mf_classic_dict_rewind,_Bool,MfClassicDict* | Function,-,mf_classic_dict_rewind,_Bool,MfClassicDict* | ||||||
| Function,-,mf_classic_emulator,_Bool,"MfClassicEmulator*, FuriHalNfcTxRxContext*" | Function,-,mf_classic_emulator,_Bool,"MfClassicEmulator*, FuriHalNfcTxRxContext*" | ||||||
| Function,-,mf_classic_get_classic_type,MfClassicType,"int8_t, uint8_t, uint8_t" | Function,-,mf_classic_get_classic_type,MfClassicType,"uint8_t, uint8_t, uint8_t" | ||||||
| Function,-,mf_classic_get_read_sectors_and_keys,void,"MfClassicData*, uint8_t*, uint8_t*" | Function,-,mf_classic_get_read_sectors_and_keys,void,"MfClassicData*, uint8_t*, uint8_t*" | ||||||
| Function,-,mf_classic_get_sector_by_block,uint8_t,uint8_t | Function,-,mf_classic_get_sector_by_block,uint8_t,uint8_t | ||||||
| Function,-,mf_classic_get_sector_trailer_block_num_by_sector,uint8_t,uint8_t | Function,-,mf_classic_get_sector_trailer_block_num_by_sector,uint8_t,uint8_t | ||||||
| @ -2065,7 +2069,7 @@ Function,-,posix_memalign,int,"void**, size_t, size_t" | |||||||
| Function,-,pow,double,"double, double" | Function,-,pow,double,"double, double" | ||||||
| Function,-,pow10,double,double | Function,-,pow10,double,double | ||||||
| Function,-,pow10f,float,float | Function,-,pow10f,float,float | ||||||
| Function,-,power_enable_low_battery_level_notification,void,"Power*, _Bool" | Function,+,power_enable_low_battery_level_notification,void,"Power*, _Bool" | ||||||
| Function,+,power_get_info,void,"Power*, PowerInfo*" | Function,+,power_get_info,void,"Power*, PowerInfo*" | ||||||
| Function,+,power_get_pubsub,FuriPubSub*,Power* | Function,+,power_get_pubsub,FuriPubSub*,Power* | ||||||
| Function,+,power_is_battery_healthy,_Bool,Power* | Function,+,power_is_battery_healthy,_Bool,Power* | ||||||
| @ -2803,6 +2807,9 @@ Function,-,vTimerSetTimerNumber,void,"TimerHandle_t, UBaseType_t" | |||||||
| Function,+,validator_is_file_alloc_init,ValidatorIsFile*,"const char*, const char*, const char*" | Function,+,validator_is_file_alloc_init,ValidatorIsFile*,"const char*, const char*, const char*" | ||||||
| Function,+,validator_is_file_callback,_Bool,"const char*, FuriString*, void*" | Function,+,validator_is_file_callback,_Bool,"const char*, FuriString*, void*" | ||||||
| Function,+,validator_is_file_free,void,ValidatorIsFile* | Function,+,validator_is_file_free,void,ValidatorIsFile* | ||||||
|  | Function,+,value_index_bool,uint8_t,"const _Bool, const _Bool[], uint8_t" | ||||||
|  | Function,+,value_index_float,uint8_t,"const float, const float[], uint8_t" | ||||||
|  | Function,+,value_index_uint32,uint8_t,"const uint32_t, const uint32_t[], uint8_t" | ||||||
| Function,+,variable_item_get_context,void*,VariableItem* | Function,+,variable_item_get_context,void*,VariableItem* | ||||||
| Function,+,variable_item_get_current_value_index,uint8_t,VariableItem* | Function,+,variable_item_get_current_value_index,uint8_t,VariableItem* | ||||||
| Function,+,variable_item_list_add,VariableItem*,"VariableItemList*, const char*, uint8_t, VariableItemChangeCallback, void*" | Function,+,variable_item_list_add,VariableItem*,"VariableItemList*, const char*, uint8_t, VariableItemChangeCallback, void*" | ||||||
|  | |||||||
| 
 | 
| @ -779,25 +779,10 @@ uint8_t SD_GetCIDRegister(SD_CID* Cid) { | |||||||
|             Cid->ManufacturerID = CID_Tab[0]; |             Cid->ManufacturerID = CID_Tab[0]; | ||||||
| 
 | 
 | ||||||
|             /* Byte 1 */ |             /* Byte 1 */ | ||||||
|             Cid->OEM_AppliID = CID_Tab[1] << 8; |             memcpy(Cid->OEM_AppliID, CID_Tab + 1, 2); | ||||||
| 
 |  | ||||||
|             /* Byte 2 */ |  | ||||||
|             Cid->OEM_AppliID |= CID_Tab[2]; |  | ||||||
| 
 | 
 | ||||||
|             /* Byte 3 */ |             /* Byte 3 */ | ||||||
|             Cid->ProdName1 = CID_Tab[3] << 24; |             memcpy(Cid->ProdName, CID_Tab + 3, 5); | ||||||
| 
 |  | ||||||
|             /* Byte 4 */ |  | ||||||
|             Cid->ProdName1 |= CID_Tab[4] << 16; |  | ||||||
| 
 |  | ||||||
|             /* Byte 5 */ |  | ||||||
|             Cid->ProdName1 |= CID_Tab[5] << 8; |  | ||||||
| 
 |  | ||||||
|             /* Byte 6 */ |  | ||||||
|             Cid->ProdName1 |= CID_Tab[6]; |  | ||||||
| 
 |  | ||||||
|             /* Byte 7 */ |  | ||||||
|             Cid->ProdName2 = CID_Tab[7]; |  | ||||||
| 
 | 
 | ||||||
|             /* Byte 8 */ |             /* Byte 8 */ | ||||||
|             Cid->ProdRev = CID_Tab[8]; |             Cid->ProdRev = CID_Tab[8]; | ||||||
| @ -815,11 +800,12 @@ uint8_t SD_GetCIDRegister(SD_CID* Cid) { | |||||||
|             Cid->ProdSN |= CID_Tab[12]; |             Cid->ProdSN |= CID_Tab[12]; | ||||||
| 
 | 
 | ||||||
|             /* Byte 13 */ |             /* Byte 13 */ | ||||||
|             Cid->Reserved1 |= (CID_Tab[13] & 0xF0) >> 4; |             Cid->Reserved1 = (CID_Tab[13] & 0xF0) >> 4; | ||||||
|             Cid->ManufactDate = (CID_Tab[13] & 0x0F) << 8; |             Cid->ManufactYear = (CID_Tab[13] & 0x0F) << 4; | ||||||
| 
 | 
 | ||||||
|             /* Byte 14 */ |             /* Byte 14 */ | ||||||
|             Cid->ManufactDate |= CID_Tab[14]; |             Cid->ManufactYear |= (CID_Tab[14] & 0xF0) >> 4; | ||||||
|  |             Cid->ManufactMonth = (CID_Tab[14] & 0x0F); | ||||||
| 
 | 
 | ||||||
|             /* Byte 15 */ |             /* Byte 15 */ | ||||||
|             Cid->CID_CRC = (CID_Tab[15] & 0xFE) >> 1; |             Cid->CID_CRC = (CID_Tab[15] & 0xFE) >> 1; | ||||||
| @ -837,6 +823,21 @@ uint8_t SD_GetCIDRegister(SD_CID* Cid) { | |||||||
|     return retr; |     return retr; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | uint8_t BSP_SD_GetCIDRegister(SD_CID* Cid) { | ||||||
|  |     uint8_t retr = BSP_SD_ERROR; | ||||||
|  | 
 | ||||||
|  |     /* Slow speed init */ | ||||||
|  |     furi_hal_spi_acquire(&furi_hal_spi_bus_handle_sd_slow); | ||||||
|  |     furi_hal_sd_spi_handle = &furi_hal_spi_bus_handle_sd_slow; | ||||||
|  | 
 | ||||||
|  |     memset(Cid, 0, sizeof(SD_CID)); | ||||||
|  |     retr = SD_GetCIDRegister(Cid); | ||||||
|  | 
 | ||||||
|  |     furi_hal_sd_spi_handle = NULL; | ||||||
|  |     furi_hal_spi_release(&furi_hal_spi_bus_handle_sd_slow); | ||||||
|  |     return retr; | ||||||
|  | } | ||||||
|  | 
 | ||||||
| /**
 | /**
 | ||||||
|   * @brief  Sends 5 bytes command to the SD card and get response |   * @brief  Sends 5 bytes command to the SD card and get response | ||||||
|   * @param  Cmd: The user expected command to send to SD card. |   * @param  Cmd: The user expected command to send to SD card. | ||||||
|  | |||||||
| @ -133,16 +133,16 @@ typedef struct { | |||||||
|   * @brief  Card Identification Data: CID Register    |   * @brief  Card Identification Data: CID Register    | ||||||
|   */ |   */ | ||||||
| typedef struct { | typedef struct { | ||||||
|     __IO uint8_t ManufacturerID; /* ManufacturerID */ |     uint8_t ManufacturerID; /* ManufacturerID */ | ||||||
|     __IO uint16_t OEM_AppliID; /* OEM/Application ID */ |     char OEM_AppliID[2]; /* OEM/Application ID */ | ||||||
|     __IO uint32_t ProdName1; /* Product Name part1 */ |     char ProdName[5]; /* Product Name */ | ||||||
|     __IO uint8_t ProdName2; /* Product Name part2*/ |     uint8_t ProdRev; /* Product Revision */ | ||||||
|     __IO uint8_t ProdRev; /* Product Revision */ |     uint32_t ProdSN; /* Product Serial Number */ | ||||||
|     __IO uint32_t ProdSN; /* Product Serial Number */ |     uint8_t Reserved1; /* Reserved1 */ | ||||||
|     __IO uint8_t Reserved1; /* Reserved1 */ |     uint8_t ManufactYear; /* Manufacturing Year */ | ||||||
|     __IO uint16_t ManufactDate; /* Manufacturing Date */ |     uint8_t ManufactMonth; /* Manufacturing Month */ | ||||||
|     __IO uint8_t CID_CRC; /* CID CRC */ |     uint8_t CID_CRC; /* CID CRC */ | ||||||
|     __IO uint8_t Reserved2; /* always 1 */ |     uint8_t Reserved2; /* always 1 */ | ||||||
| } SD_CID; | } SD_CID; | ||||||
| 
 | 
 | ||||||
| /** 
 | /** 
 | ||||||
| @ -207,6 +207,7 @@ uint8_t | |||||||
| uint8_t BSP_SD_Erase(uint32_t StartAddr, uint32_t EndAddr); | uint8_t BSP_SD_Erase(uint32_t StartAddr, uint32_t EndAddr); | ||||||
| uint8_t BSP_SD_GetCardState(void); | uint8_t BSP_SD_GetCardState(void); | ||||||
| uint8_t BSP_SD_GetCardInfo(SD_CardInfo* pCardInfo); | uint8_t BSP_SD_GetCardInfo(SD_CardInfo* pCardInfo); | ||||||
|  | uint8_t BSP_SD_GetCIDRegister(SD_CID* Cid); | ||||||
| 
 | 
 | ||||||
| /* Link functions for SD Card peripheral*/ | /* Link functions for SD Card peripheral*/ | ||||||
| void SD_SPI_Slow_Init(void); | void SD_SPI_Slow_Init(void); | ||||||
|  | |||||||
| @ -52,30 +52,6 @@ extern "C" { | |||||||
|     } |     } | ||||||
| #endif | #endif | ||||||
| 
 | 
 | ||||||
| static inline bool furi_is_irq_context() { |  | ||||||
|     bool irq = false; |  | ||||||
|     BaseType_t state; |  | ||||||
| 
 |  | ||||||
|     if(FURI_IS_IRQ_MODE()) { |  | ||||||
|         /* Called from interrupt context */ |  | ||||||
|         irq = true; |  | ||||||
|     } else { |  | ||||||
|         /* Get FreeRTOS scheduler state */ |  | ||||||
|         state = xTaskGetSchedulerState(); |  | ||||||
| 
 |  | ||||||
|         if(state != taskSCHEDULER_NOT_STARTED) { |  | ||||||
|             /* Scheduler was started */ |  | ||||||
|             if(FURI_IS_IRQ_MASKED()) { |  | ||||||
|                 /* Interrupts are masked */ |  | ||||||
|                 irq = true; |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     /* Return context, 0: thread context, 1: IRQ context */ |  | ||||||
|     return (irq); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| #ifdef __cplusplus | #ifdef __cplusplus | ||||||
| } | } | ||||||
| #endif | #endif | ||||||
|  | |||||||
| @ -93,7 +93,11 @@ extern "C" { | |||||||
| #endif | #endif | ||||||
| 
 | 
 | ||||||
| #ifndef FURI_BIT_CLEAR | #ifndef FURI_BIT_CLEAR | ||||||
| #define FURI_BIT_CLEAR(x, n) ((x) &= ~(1UL << (n))) | #define FURI_BIT_CLEAR(x, n)    \ | ||||||
|  |     ({                          \ | ||||||
|  |         __typeof__(x) _x = (1); \ | ||||||
|  |         (x) &= ~(_x << (n));    \ | ||||||
|  |     }) | ||||||
| #endif | #endif | ||||||
| 
 | 
 | ||||||
| #define FURI_SW_MEMBARRIER() asm volatile("" : : : "memory") | #define FURI_SW_MEMBARRIER() asm volatile("" : : : "memory") | ||||||
|  | |||||||
| @ -7,8 +7,32 @@ | |||||||
| 
 | 
 | ||||||
| #include CMSIS_device_header | #include CMSIS_device_header | ||||||
| 
 | 
 | ||||||
|  | bool furi_kernel_is_irq_or_masked() { | ||||||
|  |     bool irq = false; | ||||||
|  |     BaseType_t state; | ||||||
|  | 
 | ||||||
|  |     if(FURI_IS_IRQ_MODE()) { | ||||||
|  |         /* Called from interrupt context */ | ||||||
|  |         irq = true; | ||||||
|  |     } else { | ||||||
|  |         /* Get FreeRTOS scheduler state */ | ||||||
|  |         state = xTaskGetSchedulerState(); | ||||||
|  | 
 | ||||||
|  |         if(state != taskSCHEDULER_NOT_STARTED) { | ||||||
|  |             /* Scheduler was started */ | ||||||
|  |             if(FURI_IS_IRQ_MASKED()) { | ||||||
|  |                 /* Interrupts are masked */ | ||||||
|  |                 irq = true; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /* Return context, 0: thread context, 1: IRQ context */ | ||||||
|  |     return (irq); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| int32_t furi_kernel_lock() { | int32_t furi_kernel_lock() { | ||||||
|     furi_assert(!furi_is_irq_context()); |     furi_assert(!furi_kernel_is_irq_or_masked()); | ||||||
| 
 | 
 | ||||||
|     int32_t lock; |     int32_t lock; | ||||||
| 
 | 
 | ||||||
| @ -33,7 +57,7 @@ int32_t furi_kernel_lock() { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| int32_t furi_kernel_unlock() { | int32_t furi_kernel_unlock() { | ||||||
|     furi_assert(!furi_is_irq_context()); |     furi_assert(!furi_kernel_is_irq_or_masked()); | ||||||
| 
 | 
 | ||||||
|     int32_t lock; |     int32_t lock; | ||||||
| 
 | 
 | ||||||
| @ -63,7 +87,7 @@ int32_t furi_kernel_unlock() { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| int32_t furi_kernel_restore_lock(int32_t lock) { | int32_t furi_kernel_restore_lock(int32_t lock) { | ||||||
|     furi_assert(!furi_is_irq_context()); |     furi_assert(!furi_kernel_is_irq_or_masked()); | ||||||
| 
 | 
 | ||||||
|     switch(xTaskGetSchedulerState()) { |     switch(xTaskGetSchedulerState()) { | ||||||
|     case taskSCHEDULER_SUSPENDED: |     case taskSCHEDULER_SUSPENDED: | ||||||
| @ -99,7 +123,7 @@ uint32_t furi_kernel_get_tick_frequency() { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void furi_delay_tick(uint32_t ticks) { | void furi_delay_tick(uint32_t ticks) { | ||||||
|     furi_assert(!furi_is_irq_context()); |     furi_assert(!furi_kernel_is_irq_or_masked()); | ||||||
|     if(ticks == 0U) { |     if(ticks == 0U) { | ||||||
|         taskYIELD(); |         taskYIELD(); | ||||||
|     } else { |     } else { | ||||||
| @ -108,7 +132,7 @@ void furi_delay_tick(uint32_t ticks) { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| FuriStatus furi_delay_until_tick(uint32_t tick) { | FuriStatus furi_delay_until_tick(uint32_t tick) { | ||||||
|     furi_assert(!furi_is_irq_context()); |     furi_assert(!furi_kernel_is_irq_or_masked()); | ||||||
| 
 | 
 | ||||||
|     TickType_t tcnt, delay; |     TickType_t tcnt, delay; | ||||||
|     FuriStatus stat; |     FuriStatus stat; | ||||||
| @ -137,7 +161,7 @@ FuriStatus furi_delay_until_tick(uint32_t tick) { | |||||||
| uint32_t furi_get_tick() { | uint32_t furi_get_tick() { | ||||||
|     TickType_t ticks; |     TickType_t ticks; | ||||||
| 
 | 
 | ||||||
|     if(furi_is_irq_context() != 0U) { |     if(furi_kernel_is_irq_or_masked() != 0U) { | ||||||
|         ticks = xTaskGetTickCountFromISR(); |         ticks = xTaskGetTickCountFromISR(); | ||||||
|     } else { |     } else { | ||||||
|         ticks = xTaskGetTickCount(); |         ticks = xTaskGetTickCount(); | ||||||
|  | |||||||
| @ -10,19 +10,42 @@ | |||||||
| extern "C" { | extern "C" { | ||||||
| #endif | #endif | ||||||
| 
 | 
 | ||||||
|  | /** Check if CPU is in IRQ or kernel running and IRQ is masked
 | ||||||
|  |  *  | ||||||
|  |  * Originally this primitive was born as a workaround for FreeRTOS kernel primitives shenanigans with PRIMASK. | ||||||
|  |  *  | ||||||
|  |  * Meaningful use cases are: | ||||||
|  |  *  | ||||||
|  |  * - When kernel is started and you want to ensure that you are not in IRQ or IRQ is not masked(like in critical section) | ||||||
|  |  * - When kernel is not started and you want to make sure that you are not in IRQ mode, ignoring PRIMASK. | ||||||
|  |  *  | ||||||
|  |  * As you can see there will be edge case when kernel is not started and PRIMASK is not 0 that may cause some funky behavior. | ||||||
|  |  * Most likely it will happen after kernel primitives being used, but control not yet passed to kernel. | ||||||
|  |  * It's up to you to figure out if it is safe for your code or not. | ||||||
|  |  *  | ||||||
|  |  * @return     true if CPU is in IRQ or kernel running and IRQ is masked | ||||||
|  |  */ | ||||||
|  | bool furi_kernel_is_irq_or_masked(); | ||||||
|  | 
 | ||||||
| /** Lock kernel, pause process scheduling
 | /** Lock kernel, pause process scheduling
 | ||||||
|  |  * | ||||||
|  |  * @warning This should never be called in interrupt request context. | ||||||
|  * |  * | ||||||
|  * @return     previous lock state(0 - unlocked, 1 - locked) |  * @return     previous lock state(0 - unlocked, 1 - locked) | ||||||
|  */ |  */ | ||||||
| int32_t furi_kernel_lock(); | int32_t furi_kernel_lock(); | ||||||
| 
 | 
 | ||||||
| /** Unlock kernel, resume process scheduling
 | /** Unlock kernel, resume process scheduling
 | ||||||
|  |  * | ||||||
|  |  * @warning This should never be called in interrupt request context. | ||||||
|  * |  * | ||||||
|  * @return     previous lock state(0 - unlocked, 1 - locked) |  * @return     previous lock state(0 - unlocked, 1 - locked) | ||||||
|  */ |  */ | ||||||
| int32_t furi_kernel_unlock(); | int32_t furi_kernel_unlock(); | ||||||
| 
 | 
 | ||||||
| /** Restore kernel lock state
 | /** Restore kernel lock state
 | ||||||
|  |  * | ||||||
|  |  * @warning This should never be called in interrupt request context. | ||||||
|  * |  * | ||||||
|  * @param[in]  lock  The lock state |  * @param[in]  lock  The lock state | ||||||
|  * |  * | ||||||
| @ -37,7 +60,9 @@ int32_t furi_kernel_restore_lock(int32_t lock); | |||||||
| uint32_t furi_kernel_get_tick_frequency(); | uint32_t furi_kernel_get_tick_frequency(); | ||||||
| 
 | 
 | ||||||
| /** Delay execution
 | /** Delay execution
 | ||||||
|  *  |  * | ||||||
|  |  * @warning This should never be called in interrupt request context. | ||||||
|  |  * | ||||||
|  * Also keep in mind delay is aliased to scheduler timer intervals. |  * Also keep in mind delay is aliased to scheduler timer intervals. | ||||||
|  * |  * | ||||||
|  * @param[in]  ticks  The ticks count to pause |  * @param[in]  ticks  The ticks count to pause | ||||||
| @ -45,6 +70,8 @@ uint32_t furi_kernel_get_tick_frequency(); | |||||||
| void furi_delay_tick(uint32_t ticks); | void furi_delay_tick(uint32_t ticks); | ||||||
| 
 | 
 | ||||||
| /** Delay until tick
 | /** Delay until tick
 | ||||||
|  |  * | ||||||
|  |  * @warning This should never be called in interrupt request context. | ||||||
|  * |  * | ||||||
|  * @param[in]  ticks  The tick until which kerel should delay task execution |  * @param[in]  ticks  The tick until which kerel should delay task execution | ||||||
|  * |  * | ||||||
|  | |||||||
| @ -1,11 +1,11 @@ | |||||||
|  | #include "kernel.h" | ||||||
| #include "message_queue.h" | #include "message_queue.h" | ||||||
| #include "core/common_defines.h" |  | ||||||
| #include <FreeRTOS.h> | #include <FreeRTOS.h> | ||||||
| #include <queue.h> | #include <queue.h> | ||||||
| #include "check.h" | #include "check.h" | ||||||
| 
 | 
 | ||||||
| FuriMessageQueue* furi_message_queue_alloc(uint32_t msg_count, uint32_t msg_size) { | FuriMessageQueue* furi_message_queue_alloc(uint32_t msg_count, uint32_t msg_size) { | ||||||
|     furi_assert((furi_is_irq_context() == 0U) && (msg_count > 0U) && (msg_size > 0U)); |     furi_assert((furi_kernel_is_irq_or_masked() == 0U) && (msg_count > 0U) && (msg_size > 0U)); | ||||||
| 
 | 
 | ||||||
|     QueueHandle_t handle = xQueueCreate(msg_count, msg_size); |     QueueHandle_t handle = xQueueCreate(msg_count, msg_size); | ||||||
|     furi_check(handle); |     furi_check(handle); | ||||||
| @ -14,7 +14,7 @@ FuriMessageQueue* furi_message_queue_alloc(uint32_t msg_count, uint32_t msg_size | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void furi_message_queue_free(FuriMessageQueue* instance) { | void furi_message_queue_free(FuriMessageQueue* instance) { | ||||||
|     furi_assert(furi_is_irq_context() == 0U); |     furi_assert(furi_kernel_is_irq_or_masked() == 0U); | ||||||
|     furi_assert(instance); |     furi_assert(instance); | ||||||
| 
 | 
 | ||||||
|     vQueueDelete((QueueHandle_t)instance); |     vQueueDelete((QueueHandle_t)instance); | ||||||
| @ -28,7 +28,7 @@ FuriStatus | |||||||
| 
 | 
 | ||||||
|     stat = FuriStatusOk; |     stat = FuriStatusOk; | ||||||
| 
 | 
 | ||||||
|     if(furi_is_irq_context() != 0U) { |     if(furi_kernel_is_irq_or_masked() != 0U) { | ||||||
|         if((hQueue == NULL) || (msg_ptr == NULL) || (timeout != 0U)) { |         if((hQueue == NULL) || (msg_ptr == NULL) || (timeout != 0U)) { | ||||||
|             stat = FuriStatusErrorParameter; |             stat = FuriStatusErrorParameter; | ||||||
|         } else { |         } else { | ||||||
| @ -65,7 +65,7 @@ FuriStatus furi_message_queue_get(FuriMessageQueue* instance, void* msg_ptr, uin | |||||||
| 
 | 
 | ||||||
|     stat = FuriStatusOk; |     stat = FuriStatusOk; | ||||||
| 
 | 
 | ||||||
|     if(furi_is_irq_context() != 0U) { |     if(furi_kernel_is_irq_or_masked() != 0U) { | ||||||
|         if((hQueue == NULL) || (msg_ptr == NULL) || (timeout != 0U)) { |         if((hQueue == NULL) || (msg_ptr == NULL) || (timeout != 0U)) { | ||||||
|             stat = FuriStatusErrorParameter; |             stat = FuriStatusErrorParameter; | ||||||
|         } else { |         } else { | ||||||
| @ -131,7 +131,7 @@ uint32_t furi_message_queue_get_count(FuriMessageQueue* instance) { | |||||||
| 
 | 
 | ||||||
|     if(hQueue == NULL) { |     if(hQueue == NULL) { | ||||||
|         count = 0U; |         count = 0U; | ||||||
|     } else if(furi_is_irq_context() != 0U) { |     } else if(furi_kernel_is_irq_or_masked() != 0U) { | ||||||
|         count = uxQueueMessagesWaitingFromISR(hQueue); |         count = uxQueueMessagesWaitingFromISR(hQueue); | ||||||
|     } else { |     } else { | ||||||
|         count = uxQueueMessagesWaiting(hQueue); |         count = uxQueueMessagesWaiting(hQueue); | ||||||
| @ -148,7 +148,7 @@ uint32_t furi_message_queue_get_space(FuriMessageQueue* instance) { | |||||||
| 
 | 
 | ||||||
|     if(mq == NULL) { |     if(mq == NULL) { | ||||||
|         space = 0U; |         space = 0U; | ||||||
|     } else if(furi_is_irq_context() != 0U) { |     } else if(furi_kernel_is_irq_or_masked() != 0U) { | ||||||
|         isrm = taskENTER_CRITICAL_FROM_ISR(); |         isrm = taskENTER_CRITICAL_FROM_ISR(); | ||||||
| 
 | 
 | ||||||
|         /* space = pxQueue->uxLength - pxQueue->uxMessagesWaiting; */ |         /* space = pxQueue->uxLength - pxQueue->uxMessagesWaiting; */ | ||||||
| @ -167,7 +167,7 @@ FuriStatus furi_message_queue_reset(FuriMessageQueue* instance) { | |||||||
|     QueueHandle_t hQueue = (QueueHandle_t)instance; |     QueueHandle_t hQueue = (QueueHandle_t)instance; | ||||||
|     FuriStatus stat; |     FuriStatus stat; | ||||||
| 
 | 
 | ||||||
|     if(furi_is_irq_context() != 0U) { |     if(furi_kernel_is_irq_or_masked() != 0U) { | ||||||
|         stat = FuriStatusErrorISR; |         stat = FuriStatusErrorISR; | ||||||
|     } else if(hQueue == NULL) { |     } else if(hQueue == NULL) { | ||||||
|         stat = FuriStatusErrorParameter; |         stat = FuriStatusErrorParameter; | ||||||
|  | |||||||
| @ -530,6 +530,12 @@ bool furi_thread_set_stdout_callback(FuriThreadStdoutWriteCallback callback) { | |||||||
|     return true; |     return true; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | FuriThreadStdoutWriteCallback furi_thread_get_stdout_callback() { | ||||||
|  |     FuriThread* thread = furi_thread_get_current(); | ||||||
|  | 
 | ||||||
|  |     return thread->output.write_callback; | ||||||
|  | } | ||||||
|  | 
 | ||||||
| size_t furi_thread_stdout_write(const char* data, size_t size) { | size_t furi_thread_stdout_write(const char* data, size_t size) { | ||||||
|     FuriThread* thread = furi_thread_get_current(); |     FuriThread* thread = furi_thread_get_current(); | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -227,6 +227,12 @@ const char* furi_thread_get_name(FuriThreadId thread_id); | |||||||
| 
 | 
 | ||||||
| uint32_t furi_thread_get_stack_space(FuriThreadId thread_id); | uint32_t furi_thread_get_stack_space(FuriThreadId thread_id); | ||||||
| 
 | 
 | ||||||
|  | /** Get STDOUT callback for thead
 | ||||||
|  |  * | ||||||
|  |  * @return STDOUT callback | ||||||
|  |  */ | ||||||
|  | FuriThreadStdoutWriteCallback furi_thread_get_stdout_callback(); | ||||||
|  | 
 | ||||||
| /** Set STDOUT callback for thread
 | /** Set STDOUT callback for thread
 | ||||||
|  *  |  *  | ||||||
|  * @param      callback  callback or NULL to clear |  * @param      callback  callback or NULL to clear | ||||||
|  | |||||||
| @ -3,7 +3,6 @@ | |||||||
| #include "memmgr.h" | #include "memmgr.h" | ||||||
| #include "kernel.h" | #include "kernel.h" | ||||||
| 
 | 
 | ||||||
| #include "core/common_defines.h" |  | ||||||
| #include <FreeRTOS.h> | #include <FreeRTOS.h> | ||||||
| #include <timers.h> | #include <timers.h> | ||||||
| 
 | 
 | ||||||
| @ -27,7 +26,7 @@ static void TimerCallback(TimerHandle_t hTimer) { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| FuriTimer* furi_timer_alloc(FuriTimerCallback func, FuriTimerType type, void* context) { | FuriTimer* furi_timer_alloc(FuriTimerCallback func, FuriTimerType type, void* context) { | ||||||
|     furi_assert((furi_is_irq_context() == 0U) && (func != NULL)); |     furi_assert((furi_kernel_is_irq_or_masked() == 0U) && (func != NULL)); | ||||||
| 
 | 
 | ||||||
|     TimerHandle_t hTimer; |     TimerHandle_t hTimer; | ||||||
|     TimerCallback_t* callb; |     TimerCallback_t* callb; | ||||||
| @ -60,7 +59,7 @@ FuriTimer* furi_timer_alloc(FuriTimerCallback func, FuriTimerType type, void* co | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void furi_timer_free(FuriTimer* instance) { | void furi_timer_free(FuriTimer* instance) { | ||||||
|     furi_assert(!furi_is_irq_context()); |     furi_assert(!furi_kernel_is_irq_or_masked()); | ||||||
|     furi_assert(instance); |     furi_assert(instance); | ||||||
| 
 | 
 | ||||||
|     TimerHandle_t hTimer = (TimerHandle_t)instance; |     TimerHandle_t hTimer = (TimerHandle_t)instance; | ||||||
| @ -82,7 +81,7 @@ void furi_timer_free(FuriTimer* instance) { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| FuriStatus furi_timer_start(FuriTimer* instance, uint32_t ticks) { | FuriStatus furi_timer_start(FuriTimer* instance, uint32_t ticks) { | ||||||
|     furi_assert(!furi_is_irq_context()); |     furi_assert(!furi_kernel_is_irq_or_masked()); | ||||||
|     furi_assert(instance); |     furi_assert(instance); | ||||||
| 
 | 
 | ||||||
|     TimerHandle_t hTimer = (TimerHandle_t)instance; |     TimerHandle_t hTimer = (TimerHandle_t)instance; | ||||||
| @ -99,7 +98,7 @@ FuriStatus furi_timer_start(FuriTimer* instance, uint32_t ticks) { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| FuriStatus furi_timer_stop(FuriTimer* instance) { | FuriStatus furi_timer_stop(FuriTimer* instance) { | ||||||
|     furi_assert(!furi_is_irq_context()); |     furi_assert(!furi_kernel_is_irq_or_masked()); | ||||||
|     furi_assert(instance); |     furi_assert(instance); | ||||||
| 
 | 
 | ||||||
|     TimerHandle_t hTimer = (TimerHandle_t)instance; |     TimerHandle_t hTimer = (TimerHandle_t)instance; | ||||||
| @ -117,7 +116,7 @@ FuriStatus furi_timer_stop(FuriTimer* instance) { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| uint32_t furi_timer_is_running(FuriTimer* instance) { | uint32_t furi_timer_is_running(FuriTimer* instance) { | ||||||
|     furi_assert(!furi_is_irq_context()); |     furi_assert(!furi_kernel_is_irq_or_masked()); | ||||||
|     furi_assert(instance); |     furi_assert(instance); | ||||||
| 
 | 
 | ||||||
|     TimerHandle_t hTimer = (TimerHandle_t)instance; |     TimerHandle_t hTimer = (TimerHandle_t)instance; | ||||||
|  | |||||||
| @ -3,7 +3,7 @@ | |||||||
| #include "queue.h" | #include "queue.h" | ||||||
| 
 | 
 | ||||||
| void furi_init() { | void furi_init() { | ||||||
|     furi_assert(!furi_is_irq_context()); |     furi_assert(!furi_kernel_is_irq_or_masked()); | ||||||
|     furi_assert(xTaskGetSchedulerState() == taskSCHEDULER_NOT_STARTED); |     furi_assert(xTaskGetSchedulerState() == taskSCHEDULER_NOT_STARTED); | ||||||
| 
 | 
 | ||||||
|     furi_log_init(); |     furi_log_init(); | ||||||
| @ -11,7 +11,7 @@ void furi_init() { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void furi_run() { | void furi_run() { | ||||||
|     furi_assert(!furi_is_irq_context()); |     furi_assert(!furi_kernel_is_irq_or_masked()); | ||||||
|     furi_assert(xTaskGetSchedulerState() == taskSCHEDULER_NOT_STARTED); |     furi_assert(xTaskGetSchedulerState() == taskSCHEDULER_NOT_STARTED); | ||||||
| 
 | 
 | ||||||
| #if(__ARM_ARCH_7A__ == 0U) | #if(__ARM_ARCH_7A__ == 0U) | ||||||
|  | |||||||
| @ -315,6 +315,7 @@ static bool elf_relocate_symbol(ELFFile* elf, Elf32_Addr relAddr, int type, Elf3 | |||||||
|         FURI_LOG_D(TAG, "  R_ARM_ABS32 relocated is 0x%08X", (unsigned int)*((uint32_t*)relAddr)); |         FURI_LOG_D(TAG, "  R_ARM_ABS32 relocated is 0x%08X", (unsigned int)*((uint32_t*)relAddr)); | ||||||
|         break; |         break; | ||||||
|     case R_ARM_THM_PC22: |     case R_ARM_THM_PC22: | ||||||
|  |     case R_ARM_CALL: | ||||||
|     case R_ARM_THM_JUMP24: |     case R_ARM_THM_JUMP24: | ||||||
|         elf_relocate_jmp_call(elf, relAddr, type, symAddr); |         elf_relocate_jmp_call(elf, relAddr, type, symAddr); | ||||||
|         FURI_LOG_D( |         FURI_LOG_D( | ||||||
|  | |||||||
| @ -1,5 +1,6 @@ | |||||||
| #include "flipper_application.h" | #include "flipper_application.h" | ||||||
| #include "elf/elf_file.h" | #include "elf/elf_file.h" | ||||||
|  | #include <notification/notification_messages.h> | ||||||
| 
 | 
 | ||||||
| #define TAG "fapp" | #define TAG "fapp" | ||||||
| 
 | 
 | ||||||
| @ -95,6 +96,15 @@ static int32_t flipper_application_thread(void* context) { | |||||||
|     elf_file_pre_run(last_loaded_app->elf); |     elf_file_pre_run(last_loaded_app->elf); | ||||||
|     int32_t result = elf_file_run(last_loaded_app->elf, context); |     int32_t result = elf_file_run(last_loaded_app->elf, context); | ||||||
|     elf_file_post_run(last_loaded_app->elf); |     elf_file_post_run(last_loaded_app->elf); | ||||||
|  | 
 | ||||||
|  |     // wait until all notifications from RAM are completed
 | ||||||
|  |     NotificationApp* notifications = furi_record_open(RECORD_NOTIFICATION); | ||||||
|  |     const NotificationSequence sequence_empty = { | ||||||
|  |         NULL, | ||||||
|  |     }; | ||||||
|  |     notification_message_block(notifications, &sequence_empty); | ||||||
|  |     furi_record_close(RECORD_NOTIFICATION); | ||||||
|  | 
 | ||||||
|     return result; |     return result; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -1,11 +1,8 @@ | |||||||
|  | #include "infrared_common_i.h" | ||||||
|  | 
 | ||||||
|  | #include <stdlib.h> | ||||||
| #include <core/check.h> | #include <core/check.h> | ||||||
| #include <core/common_defines.h> | #include <core/common_defines.h> | ||||||
| #include "infrared.h" |  | ||||||
| #include "infrared_common_i.h" |  | ||||||
| #include <stdbool.h> |  | ||||||
| #include <furi.h> |  | ||||||
| #include "infrared_i.h" |  | ||||||
| #include <stdint.h> |  | ||||||
| 
 | 
 | ||||||
| static void infrared_common_decoder_reset_state(InfraredCommonDecoder* decoder); | static void infrared_common_decoder_reset_state(InfraredCommonDecoder* decoder); | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -1,10 +1,9 @@ | |||||||
| #include <core/check.h> |  | ||||||
| #include "infrared.h" |  | ||||||
| #include "infrared_common_i.h" | #include "infrared_common_i.h" | ||||||
| #include <stdbool.h> | 
 | ||||||
| #include <furi.h> | #include <stdlib.h> | ||||||
| #include "infrared_i.h" | #include <string.h> | ||||||
| #include <stdint.h> | #include <core/check.h> | ||||||
|  | #include <core/common_defines.h> | ||||||
| 
 | 
 | ||||||
| static InfraredStatus | static InfraredStatus | ||||||
|     infrared_common_encode_bits(InfraredCommonEncoder* encoder, uint32_t* duration, bool* level) { |     infrared_common_encode_bits(InfraredCommonEncoder* encoder, uint32_t* duration, bool* level) { | ||||||
|  | |||||||
| @ -1,140 +0,0 @@ | |||||||
| #include "infrared_common_i.h" |  | ||||||
| #include "infrared_protocol_defs_i.h" |  | ||||||
| 
 |  | ||||||
| const InfraredCommonProtocolSpec protocol_nec = { |  | ||||||
|     .timings = |  | ||||||
|         { |  | ||||||
|             .preamble_mark = INFRARED_NEC_PREAMBLE_MARK, |  | ||||||
|             .preamble_space = INFRARED_NEC_PREAMBLE_SPACE, |  | ||||||
|             .bit1_mark = INFRARED_NEC_BIT1_MARK, |  | ||||||
|             .bit1_space = INFRARED_NEC_BIT1_SPACE, |  | ||||||
|             .bit0_mark = INFRARED_NEC_BIT0_MARK, |  | ||||||
|             .bit0_space = INFRARED_NEC_BIT0_SPACE, |  | ||||||
|             .preamble_tolerance = INFRARED_NEC_PREAMBLE_TOLERANCE, |  | ||||||
|             .bit_tolerance = INFRARED_NEC_BIT_TOLERANCE, |  | ||||||
|             .silence_time = INFRARED_NEC_SILENCE, |  | ||||||
|             .min_split_time = INFRARED_NEC_MIN_SPLIT_TIME, |  | ||||||
|         }, |  | ||||||
|     .databit_len[0] = 42, |  | ||||||
|     .databit_len[1] = 32, |  | ||||||
|     .no_stop_bit = false, |  | ||||||
|     .decode = infrared_common_decode_pdwm, |  | ||||||
|     .encode = infrared_common_encode_pdwm, |  | ||||||
|     .interpret = infrared_decoder_nec_interpret, |  | ||||||
|     .decode_repeat = infrared_decoder_nec_decode_repeat, |  | ||||||
|     .encode_repeat = infrared_encoder_nec_encode_repeat, |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| const InfraredCommonProtocolSpec protocol_samsung32 = { |  | ||||||
|     .timings = |  | ||||||
|         { |  | ||||||
|             .preamble_mark = INFRARED_SAMSUNG_PREAMBLE_MARK, |  | ||||||
|             .preamble_space = INFRARED_SAMSUNG_PREAMBLE_SPACE, |  | ||||||
|             .bit1_mark = INFRARED_SAMSUNG_BIT1_MARK, |  | ||||||
|             .bit1_space = INFRARED_SAMSUNG_BIT1_SPACE, |  | ||||||
|             .bit0_mark = INFRARED_SAMSUNG_BIT0_MARK, |  | ||||||
|             .bit0_space = INFRARED_SAMSUNG_BIT0_SPACE, |  | ||||||
|             .preamble_tolerance = INFRARED_SAMSUNG_PREAMBLE_TOLERANCE, |  | ||||||
|             .bit_tolerance = INFRARED_SAMSUNG_BIT_TOLERANCE, |  | ||||||
|             .silence_time = INFRARED_SAMSUNG_SILENCE, |  | ||||||
|             .min_split_time = INFRARED_SAMSUNG_MIN_SPLIT_TIME, |  | ||||||
|         }, |  | ||||||
|     .databit_len[0] = 32, |  | ||||||
|     .no_stop_bit = false, |  | ||||||
|     .decode = infrared_common_decode_pdwm, |  | ||||||
|     .encode = infrared_common_encode_pdwm, |  | ||||||
|     .interpret = infrared_decoder_samsung32_interpret, |  | ||||||
|     .decode_repeat = infrared_decoder_samsung32_decode_repeat, |  | ||||||
|     .encode_repeat = infrared_encoder_samsung32_encode_repeat, |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| const InfraredCommonProtocolSpec protocol_rc6 = { |  | ||||||
|     .timings = |  | ||||||
|         { |  | ||||||
|             .preamble_mark = INFRARED_RC6_PREAMBLE_MARK, |  | ||||||
|             .preamble_space = INFRARED_RC6_PREAMBLE_SPACE, |  | ||||||
|             .bit1_mark = INFRARED_RC6_BIT, |  | ||||||
|             .preamble_tolerance = INFRARED_RC6_PREAMBLE_TOLERANCE, |  | ||||||
|             .bit_tolerance = INFRARED_RC6_BIT_TOLERANCE, |  | ||||||
|             .silence_time = INFRARED_RC6_SILENCE, |  | ||||||
|             .min_split_time = INFRARED_RC6_MIN_SPLIT_TIME, |  | ||||||
|         }, |  | ||||||
|     .databit_len[0] = |  | ||||||
|         1 + 3 + 1 + 8 + |  | ||||||
|         8, // start_bit + 3 mode bits, + 1 toggle bit (x2 timing) + 8 address + 8 command
 |  | ||||||
|     .manchester_start_from_space = false, |  | ||||||
|     .decode = infrared_decoder_rc6_decode_manchester, |  | ||||||
|     .encode = infrared_encoder_rc6_encode_manchester, |  | ||||||
|     .interpret = infrared_decoder_rc6_interpret, |  | ||||||
|     .decode_repeat = NULL, |  | ||||||
|     .encode_repeat = NULL, |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| const InfraredCommonProtocolSpec protocol_rc5 = { |  | ||||||
|     .timings = |  | ||||||
|         { |  | ||||||
|             .preamble_mark = 0, |  | ||||||
|             .preamble_space = 0, |  | ||||||
|             .bit1_mark = INFRARED_RC5_BIT, |  | ||||||
|             .preamble_tolerance = 0, |  | ||||||
|             .bit_tolerance = INFRARED_RC5_BIT_TOLERANCE, |  | ||||||
|             .silence_time = INFRARED_RC5_SILENCE, |  | ||||||
|             .min_split_time = INFRARED_RC5_MIN_SPLIT_TIME, |  | ||||||
|         }, |  | ||||||
|     .databit_len[0] = 1 + 1 + 1 + 5 + |  | ||||||
|                       6, // start_bit + start_bit/command_bit + toggle_bit + 5 address + 6 command
 |  | ||||||
|     .manchester_start_from_space = true, |  | ||||||
|     .decode = infrared_common_decode_manchester, |  | ||||||
|     .encode = infrared_common_encode_manchester, |  | ||||||
|     .interpret = infrared_decoder_rc5_interpret, |  | ||||||
|     .decode_repeat = NULL, |  | ||||||
|     .encode_repeat = NULL, |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| const InfraredCommonProtocolSpec protocol_sirc = { |  | ||||||
|     .timings = |  | ||||||
|         { |  | ||||||
|             .preamble_mark = INFRARED_SIRC_PREAMBLE_MARK, |  | ||||||
|             .preamble_space = INFRARED_SIRC_PREAMBLE_SPACE, |  | ||||||
|             .bit1_mark = INFRARED_SIRC_BIT1_MARK, |  | ||||||
|             .bit1_space = INFRARED_SIRC_BIT1_SPACE, |  | ||||||
|             .bit0_mark = INFRARED_SIRC_BIT0_MARK, |  | ||||||
|             .bit0_space = INFRARED_SIRC_BIT0_SPACE, |  | ||||||
|             .preamble_tolerance = INFRARED_SIRC_PREAMBLE_TOLERANCE, |  | ||||||
|             .bit_tolerance = INFRARED_SIRC_BIT_TOLERANCE, |  | ||||||
|             .silence_time = INFRARED_SIRC_SILENCE, |  | ||||||
|             .min_split_time = INFRARED_SIRC_MIN_SPLIT_TIME, |  | ||||||
|         }, |  | ||||||
|     .databit_len[0] = 20, |  | ||||||
|     .databit_len[1] = 15, |  | ||||||
|     .databit_len[2] = 12, |  | ||||||
|     .no_stop_bit = true, |  | ||||||
|     .decode = infrared_common_decode_pdwm, |  | ||||||
|     .encode = infrared_common_encode_pdwm, |  | ||||||
|     .interpret = infrared_decoder_sirc_interpret, |  | ||||||
|     .decode_repeat = NULL, |  | ||||||
|     .encode_repeat = infrared_encoder_sirc_encode_repeat, |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| const InfraredCommonProtocolSpec protocol_kaseikyo = { |  | ||||||
|     .timings = |  | ||||||
|         { |  | ||||||
|             .preamble_mark = INFRARED_KASEIKYO_PREAMBLE_MARK, |  | ||||||
|             .preamble_space = INFRARED_KASEIKYO_PREAMBLE_SPACE, |  | ||||||
|             .bit1_mark = INFRARED_KASEIKYO_BIT1_MARK, |  | ||||||
|             .bit1_space = INFRARED_KASEIKYO_BIT1_SPACE, |  | ||||||
|             .bit0_mark = INFRARED_KASEIKYO_BIT0_MARK, |  | ||||||
|             .bit0_space = INFRARED_KASEIKYO_BIT0_SPACE, |  | ||||||
|             .preamble_tolerance = INFRARED_KASEIKYO_PREAMBLE_TOLERANCE, |  | ||||||
|             .bit_tolerance = INFRARED_KASEIKYO_BIT_TOLERANCE, |  | ||||||
|             .silence_time = INFRARED_KASEIKYO_SILENCE, |  | ||||||
|             .min_split_time = INFRARED_KASEIKYO_MIN_SPLIT_TIME, |  | ||||||
|         }, |  | ||||||
|     .databit_len[0] = 48, |  | ||||||
|     .no_stop_bit = false, |  | ||||||
|     .decode = infrared_common_decode_pdwm, |  | ||||||
|     .encode = infrared_common_encode_pdwm, |  | ||||||
|     .interpret = infrared_decoder_kaseikyo_interpret, |  | ||||||
|     .decode_repeat = NULL, |  | ||||||
|     .encode_repeat = NULL, |  | ||||||
| }; |  | ||||||
| @ -1,13 +1,16 @@ | |||||||
| #include "infrared.h" | #include "infrared.h" | ||||||
| #include <core/check.h> | 
 | ||||||
| #include "common/infrared_common_i.h" |  | ||||||
| #include "infrared_protocol_defs_i.h" |  | ||||||
| #include <stdbool.h> |  | ||||||
| #include <stdint.h> |  | ||||||
| #include <stdlib.h> | #include <stdlib.h> | ||||||
| #include <furi.h> | #include <string.h> | ||||||
| #include "infrared_i.h" | #include <core/check.h> | ||||||
| #include <furi_hal_infrared.h> | #include <core/common_defines.h> | ||||||
|  | 
 | ||||||
|  | #include "nec/infrared_protocol_nec.h" | ||||||
|  | #include "samsung/infrared_protocol_samsung.h" | ||||||
|  | #include "rc5/infrared_protocol_rc5.h" | ||||||
|  | #include "rc6/infrared_protocol_rc6.h" | ||||||
|  | #include "sirc/infrared_protocol_sirc.h" | ||||||
|  | #include "kaseikyo/infrared_protocol_kaseikyo.h" | ||||||
| 
 | 
 | ||||||
| typedef struct { | typedef struct { | ||||||
|     InfraredAlloc alloc; |     InfraredAlloc alloc; | ||||||
| @ -36,7 +39,7 @@ struct InfraredEncoderHandler { | |||||||
| typedef struct { | typedef struct { | ||||||
|     InfraredEncoders encoder; |     InfraredEncoders encoder; | ||||||
|     InfraredDecoders decoder; |     InfraredDecoders decoder; | ||||||
|     InfraredGetProtocolSpec get_protocol_spec; |     InfraredGetProtocolVariant get_protocol_variant; | ||||||
| } InfraredEncoderDecoder; | } InfraredEncoderDecoder; | ||||||
| 
 | 
 | ||||||
| static const InfraredEncoderDecoder infrared_encoder_decoder[] = { | static const InfraredEncoderDecoder infrared_encoder_decoder[] = { | ||||||
| @ -52,7 +55,7 @@ static const InfraredEncoderDecoder infrared_encoder_decoder[] = { | |||||||
|              .encode = infrared_encoder_nec_encode, |              .encode = infrared_encoder_nec_encode, | ||||||
|              .reset = infrared_encoder_nec_reset, |              .reset = infrared_encoder_nec_reset, | ||||||
|              .free = infrared_encoder_nec_free}, |              .free = infrared_encoder_nec_free}, | ||||||
|         .get_protocol_spec = infrared_nec_get_spec, |         .get_protocol_variant = infrared_protocol_nec_get_variant, | ||||||
|     }, |     }, | ||||||
|     { |     { | ||||||
|         .decoder = |         .decoder = | ||||||
| @ -66,7 +69,7 @@ static const InfraredEncoderDecoder infrared_encoder_decoder[] = { | |||||||
|              .encode = infrared_encoder_samsung32_encode, |              .encode = infrared_encoder_samsung32_encode, | ||||||
|              .reset = infrared_encoder_samsung32_reset, |              .reset = infrared_encoder_samsung32_reset, | ||||||
|              .free = infrared_encoder_samsung32_free}, |              .free = infrared_encoder_samsung32_free}, | ||||||
|         .get_protocol_spec = infrared_samsung32_get_spec, |         .get_protocol_variant = infrared_protocol_samsung32_get_variant, | ||||||
|     }, |     }, | ||||||
|     { |     { | ||||||
|         .decoder = |         .decoder = | ||||||
| @ -80,7 +83,7 @@ static const InfraredEncoderDecoder infrared_encoder_decoder[] = { | |||||||
|              .encode = infrared_encoder_rc5_encode, |              .encode = infrared_encoder_rc5_encode, | ||||||
|              .reset = infrared_encoder_rc5_reset, |              .reset = infrared_encoder_rc5_reset, | ||||||
|              .free = infrared_encoder_rc5_free}, |              .free = infrared_encoder_rc5_free}, | ||||||
|         .get_protocol_spec = infrared_rc5_get_spec, |         .get_protocol_variant = infrared_protocol_rc5_get_variant, | ||||||
|     }, |     }, | ||||||
|     { |     { | ||||||
|         .decoder = |         .decoder = | ||||||
| @ -94,7 +97,7 @@ static const InfraredEncoderDecoder infrared_encoder_decoder[] = { | |||||||
|              .encode = infrared_encoder_rc6_encode, |              .encode = infrared_encoder_rc6_encode, | ||||||
|              .reset = infrared_encoder_rc6_reset, |              .reset = infrared_encoder_rc6_reset, | ||||||
|              .free = infrared_encoder_rc6_free}, |              .free = infrared_encoder_rc6_free}, | ||||||
|         .get_protocol_spec = infrared_rc6_get_spec, |         .get_protocol_variant = infrared_protocol_rc6_get_variant, | ||||||
|     }, |     }, | ||||||
|     { |     { | ||||||
|         .decoder = |         .decoder = | ||||||
| @ -108,7 +111,7 @@ static const InfraredEncoderDecoder infrared_encoder_decoder[] = { | |||||||
|              .encode = infrared_encoder_sirc_encode, |              .encode = infrared_encoder_sirc_encode, | ||||||
|              .reset = infrared_encoder_sirc_reset, |              .reset = infrared_encoder_sirc_reset, | ||||||
|              .free = infrared_encoder_sirc_free}, |              .free = infrared_encoder_sirc_free}, | ||||||
|         .get_protocol_spec = infrared_sirc_get_spec, |         .get_protocol_variant = infrared_protocol_sirc_get_variant, | ||||||
|     }, |     }, | ||||||
|     { |     { | ||||||
|         .decoder = |         .decoder = | ||||||
| @ -122,13 +125,12 @@ static const InfraredEncoderDecoder infrared_encoder_decoder[] = { | |||||||
|              .encode = infrared_encoder_kaseikyo_encode, |              .encode = infrared_encoder_kaseikyo_encode, | ||||||
|              .reset = infrared_encoder_kaseikyo_reset, |              .reset = infrared_encoder_kaseikyo_reset, | ||||||
|              .free = infrared_encoder_kaseikyo_free}, |              .free = infrared_encoder_kaseikyo_free}, | ||||||
|         .get_protocol_spec = infrared_kaseikyo_get_spec, |         .get_protocol_variant = infrared_protocol_kaseikyo_get_variant, | ||||||
|     }, |     }, | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| static int infrared_find_index_by_protocol(InfraredProtocol protocol); | static int infrared_find_index_by_protocol(InfraredProtocol protocol); | ||||||
| static const InfraredProtocolSpecification* | static const InfraredProtocolVariant* infrared_get_variant_by_protocol(InfraredProtocol protocol); | ||||||
|     infrared_get_spec_by_protocol(InfraredProtocol protocol); |  | ||||||
| 
 | 
 | ||||||
| const InfraredMessage* | const InfraredMessage* | ||||||
|     infrared_decode(InfraredDecoderHandler* handler, bool level, uint32_t duration) { |     infrared_decode(InfraredDecoderHandler* handler, bool level, uint32_t duration) { | ||||||
| @ -224,7 +226,7 @@ void infrared_free_encoder(InfraredEncoderHandler* handler) { | |||||||
| 
 | 
 | ||||||
| static int infrared_find_index_by_protocol(InfraredProtocol protocol) { | static int infrared_find_index_by_protocol(InfraredProtocol protocol) { | ||||||
|     for(size_t i = 0; i < COUNT_OF(infrared_encoder_decoder); ++i) { |     for(size_t i = 0; i < COUNT_OF(infrared_encoder_decoder); ++i) { | ||||||
|         if(infrared_encoder_decoder[i].get_protocol_spec(protocol)) { |         if(infrared_encoder_decoder[i].get_protocol_variant(protocol)) { | ||||||
|             return i; |             return i; | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| @ -282,34 +284,37 @@ InfraredProtocol infrared_get_protocol_by_name(const char* protocol_name) { | |||||||
|     return InfraredProtocolUnknown; |     return InfraredProtocolUnknown; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static const InfraredProtocolSpecification* | static const InfraredProtocolVariant* infrared_get_variant_by_protocol(InfraredProtocol protocol) { | ||||||
|     infrared_get_spec_by_protocol(InfraredProtocol protocol) { |  | ||||||
|     int index = infrared_find_index_by_protocol(protocol); |     int index = infrared_find_index_by_protocol(protocol); | ||||||
|     const InfraredProtocolSpecification* spec = NULL; |     const InfraredProtocolVariant* variant = NULL; | ||||||
|     if(index >= 0) { |     if(index >= 0) { | ||||||
|         spec = infrared_encoder_decoder[index].get_protocol_spec(protocol); |         variant = infrared_encoder_decoder[index].get_protocol_variant(protocol); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     furi_assert(spec); |     furi_assert(variant); | ||||||
|     return spec; |     return variant; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| const char* infrared_get_protocol_name(InfraredProtocol protocol) { | const char* infrared_get_protocol_name(InfraredProtocol protocol) { | ||||||
|     return infrared_get_spec_by_protocol(protocol)->name; |     return infrared_get_variant_by_protocol(protocol)->name; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| uint8_t infrared_get_protocol_address_length(InfraredProtocol protocol) { | uint8_t infrared_get_protocol_address_length(InfraredProtocol protocol) { | ||||||
|     return infrared_get_spec_by_protocol(protocol)->address_length; |     return infrared_get_variant_by_protocol(protocol)->address_length; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| uint8_t infrared_get_protocol_command_length(InfraredProtocol protocol) { | uint8_t infrared_get_protocol_command_length(InfraredProtocol protocol) { | ||||||
|     return infrared_get_spec_by_protocol(protocol)->command_length; |     return infrared_get_variant_by_protocol(protocol)->command_length; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| uint32_t infrared_get_protocol_frequency(InfraredProtocol protocol) { | uint32_t infrared_get_protocol_frequency(InfraredProtocol protocol) { | ||||||
|     return infrared_get_spec_by_protocol(protocol)->frequency; |     return infrared_get_variant_by_protocol(protocol)->frequency; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| float infrared_get_protocol_duty_cycle(InfraredProtocol protocol) { | float infrared_get_protocol_duty_cycle(InfraredProtocol protocol) { | ||||||
|     return infrared_get_spec_by_protocol(protocol)->duty_cycle; |     return infrared_get_variant_by_protocol(protocol)->duty_cycle; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | size_t infrared_get_protocol_min_repeat_count(InfraredProtocol protocol) { | ||||||
|  |     return infrared_get_variant_by_protocol(protocol)->repeat_count; | ||||||
| } | } | ||||||
|  | |||||||
| @ -1,6 +1,7 @@ | |||||||
| #pragma once | #pragma once | ||||||
| 
 | 
 | ||||||
| #include <stdbool.h> | #include <stdbool.h> | ||||||
|  | #include <stddef.h> | ||||||
| #include <stdint.h> | #include <stdint.h> | ||||||
| 
 | 
 | ||||||
| #ifdef __cplusplus | #ifdef __cplusplus | ||||||
| @ -201,6 +202,15 @@ uint32_t infrared_get_protocol_frequency(InfraredProtocol protocol); | |||||||
|  */ |  */ | ||||||
| float infrared_get_protocol_duty_cycle(InfraredProtocol protocol); | float infrared_get_protocol_duty_cycle(InfraredProtocol protocol); | ||||||
| 
 | 
 | ||||||
|  | /**
 | ||||||
|  |  * Get the minimum count of signal repeats for the selected protocol | ||||||
|  |  * | ||||||
|  |  * \param[in]   protocol    - protocol to get the repeat count from | ||||||
|  |  * | ||||||
|  |  * \return      repeat count | ||||||
|  |  */ | ||||||
|  | size_t infrared_get_protocol_min_repeat_count(InfraredProtocol protocol); | ||||||
|  | 
 | ||||||
| #ifdef __cplusplus | #ifdef __cplusplus | ||||||
| } | } | ||||||
| #endif | #endif | ||||||
|  | |||||||
| @ -22,9 +22,10 @@ typedef struct { | |||||||
|     uint8_t command_length; |     uint8_t command_length; | ||||||
|     uint32_t frequency; |     uint32_t frequency; | ||||||
|     float duty_cycle; |     float duty_cycle; | ||||||
| } InfraredProtocolSpecification; |     size_t repeat_count; | ||||||
|  | } InfraredProtocolVariant; | ||||||
| 
 | 
 | ||||||
| typedef const InfraredProtocolSpecification* (*InfraredGetProtocolSpec)(InfraredProtocol protocol); | typedef const InfraredProtocolVariant* (*InfraredGetProtocolVariant)(InfraredProtocol protocol); | ||||||
| 
 | 
 | ||||||
| typedef void* (*InfraredAlloc)(void); | typedef void* (*InfraredAlloc)(void); | ||||||
| typedef void (*InfraredFree)(void*); | typedef void (*InfraredFree)(void*); | ||||||
|  | |||||||
| @ -1,320 +0,0 @@ | |||||||
| #pragma once |  | ||||||
| 
 |  | ||||||
| #include <stddef.h> |  | ||||||
| #include <stdbool.h> |  | ||||||
| #include <stdint.h> |  | ||||||
| #include "infrared.h" |  | ||||||
| #include "common/infrared_common_i.h" |  | ||||||
| 
 |  | ||||||
| /***************************************************************************************************
 |  | ||||||
| *   NEC protocol description |  | ||||||
| *   https://radioparty.ru/manuals/encyclopedia/213-ircontrol?start=1
 |  | ||||||
| **************************************************************************************************** |  | ||||||
| *     Preamble   Preamble      Pulse Distance/Width          Pause       Preamble   Preamble  Stop |  | ||||||
| *       mark      space            Modulation             up to period    repeat     repeat    bit |  | ||||||
| *                                                                          mark       space |  | ||||||
| * |  | ||||||
| *        9000      4500         32 bit + stop bit         ...110000         9000       2250 |  | ||||||
| *     __________          _ _ _ _  _  _  _ _ _  _  _ _ _                ___________            _ |  | ||||||
| * ____          __________ _ _ _ __ __ __ _ _ __ __ _ _ ________________           ____________ ___ |  | ||||||
| * |  | ||||||
| ***************************************************************************************************/ |  | ||||||
| 
 |  | ||||||
| #define INFRARED_NEC_PREAMBLE_MARK 9000 |  | ||||||
| #define INFRARED_NEC_PREAMBLE_SPACE 4500 |  | ||||||
| #define INFRARED_NEC_BIT1_MARK 560 |  | ||||||
| #define INFRARED_NEC_BIT1_SPACE 1690 |  | ||||||
| #define INFRARED_NEC_BIT0_MARK 560 |  | ||||||
| #define INFRARED_NEC_BIT0_SPACE 560 |  | ||||||
| #define INFRARED_NEC_REPEAT_PERIOD 110000 |  | ||||||
| #define INFRARED_NEC_SILENCE INFRARED_NEC_REPEAT_PERIOD |  | ||||||
| #define INFRARED_NEC_MIN_SPLIT_TIME INFRARED_NEC_REPEAT_PAUSE_MIN |  | ||||||
| #define INFRARED_NEC_REPEAT_PAUSE_MIN 4000 |  | ||||||
| #define INFRARED_NEC_REPEAT_PAUSE_MAX 150000 |  | ||||||
| #define INFRARED_NEC_REPEAT_MARK 9000 |  | ||||||
| #define INFRARED_NEC_REPEAT_SPACE 2250 |  | ||||||
| #define INFRARED_NEC_PREAMBLE_TOLERANCE 200 // us
 |  | ||||||
| #define INFRARED_NEC_BIT_TOLERANCE 120 // us
 |  | ||||||
| 
 |  | ||||||
| void* infrared_decoder_nec_alloc(void); |  | ||||||
| void infrared_decoder_nec_reset(void* decoder); |  | ||||||
| void infrared_decoder_nec_free(void* decoder); |  | ||||||
| InfraredMessage* infrared_decoder_nec_check_ready(void* decoder); |  | ||||||
| InfraredMessage* infrared_decoder_nec_decode(void* decoder, bool level, uint32_t duration); |  | ||||||
| void* infrared_encoder_nec_alloc(void); |  | ||||||
| InfraredStatus infrared_encoder_nec_encode(void* encoder_ptr, uint32_t* duration, bool* level); |  | ||||||
| void infrared_encoder_nec_reset(void* encoder_ptr, const InfraredMessage* message); |  | ||||||
| void infrared_encoder_nec_free(void* encoder_ptr); |  | ||||||
| bool infrared_decoder_nec_interpret(InfraredCommonDecoder* decoder); |  | ||||||
| InfraredStatus infrared_decoder_nec_decode_repeat(InfraredCommonDecoder* decoder); |  | ||||||
| InfraredStatus infrared_encoder_nec_encode_repeat( |  | ||||||
|     InfraredCommonEncoder* encoder, |  | ||||||
|     uint32_t* duration, |  | ||||||
|     bool* level); |  | ||||||
| const InfraredProtocolSpecification* infrared_nec_get_spec(InfraredProtocol protocol); |  | ||||||
| 
 |  | ||||||
| extern const InfraredCommonProtocolSpec protocol_nec; |  | ||||||
| 
 |  | ||||||
| /***************************************************************************************************
 |  | ||||||
| *   SAMSUNG32 protocol description |  | ||||||
| *   https://www.mikrocontroller.net/articles/IRMP_-_english#SAMSUNG
 |  | ||||||
| **************************************************************************************************** |  | ||||||
| *  Preamble   Preamble     Pulse Distance/Width        Pause       Preamble   Preamble  Bit1  Stop |  | ||||||
| *    mark      space           Modulation                           repeat     repeat          bit |  | ||||||
| *                                                                    mark       space |  | ||||||
| * |  | ||||||
| *     4500      4500        32 bit + stop bit       40000/100000     4500       4500 |  | ||||||
| *  __________          _  _ _  _  _  _ _ _  _  _ _                ___________            _    _ |  | ||||||
| * _          __________ __ _ __ __ __ _ _ __ __ _ ________________           ____________ ____ ___ |  | ||||||
| * |  | ||||||
| ***************************************************************************************************/ |  | ||||||
| 
 |  | ||||||
| #define INFRARED_SAMSUNG_PREAMBLE_MARK 4500 |  | ||||||
| #define INFRARED_SAMSUNG_PREAMBLE_SPACE 4500 |  | ||||||
| #define INFRARED_SAMSUNG_BIT1_MARK 550 |  | ||||||
| #define INFRARED_SAMSUNG_BIT1_SPACE 1650 |  | ||||||
| #define INFRARED_SAMSUNG_BIT0_MARK 550 |  | ||||||
| #define INFRARED_SAMSUNG_BIT0_SPACE 550 |  | ||||||
| #define INFRARED_SAMSUNG_REPEAT_PAUSE_MIN 30000 |  | ||||||
| #define INFRARED_SAMSUNG_REPEAT_PAUSE1 46000 |  | ||||||
| #define INFRARED_SAMSUNG_REPEAT_PAUSE2 97000 |  | ||||||
| /* Samsung silence have to be greater than REPEAT MAX
 |  | ||||||
|  * otherwise there can be problems during unit tests parsing |  | ||||||
|  * of some data. Real tolerances we don't know, but in real life |  | ||||||
|  * silence time should be greater than max repeat time. This is |  | ||||||
|  * because of similar preambule timings for repeat and first messages. */ |  | ||||||
| #define INFRARED_SAMSUNG_MIN_SPLIT_TIME 5000 |  | ||||||
| #define INFRARED_SAMSUNG_SILENCE 145000 |  | ||||||
| #define INFRARED_SAMSUNG_REPEAT_PAUSE_MAX 140000 |  | ||||||
| #define INFRARED_SAMSUNG_REPEAT_MARK 4500 |  | ||||||
| #define INFRARED_SAMSUNG_REPEAT_SPACE 4500 |  | ||||||
| #define INFRARED_SAMSUNG_PREAMBLE_TOLERANCE 200 // us
 |  | ||||||
| #define INFRARED_SAMSUNG_BIT_TOLERANCE 120 // us
 |  | ||||||
| 
 |  | ||||||
| void* infrared_decoder_samsung32_alloc(void); |  | ||||||
| void infrared_decoder_samsung32_reset(void* decoder); |  | ||||||
| void infrared_decoder_samsung32_free(void* decoder); |  | ||||||
| InfraredMessage* infrared_decoder_samsung32_check_ready(void* ctx); |  | ||||||
| InfraredMessage* infrared_decoder_samsung32_decode(void* decoder, bool level, uint32_t duration); |  | ||||||
| InfraredStatus |  | ||||||
|     infrared_encoder_samsung32_encode(void* encoder_ptr, uint32_t* duration, bool* level); |  | ||||||
| void infrared_encoder_samsung32_reset(void* encoder_ptr, const InfraredMessage* message); |  | ||||||
| void* infrared_encoder_samsung32_alloc(void); |  | ||||||
| void infrared_encoder_samsung32_free(void* encoder_ptr); |  | ||||||
| bool infrared_decoder_samsung32_interpret(InfraredCommonDecoder* decoder); |  | ||||||
| InfraredStatus infrared_decoder_samsung32_decode_repeat(InfraredCommonDecoder* decoder); |  | ||||||
| InfraredStatus infrared_encoder_samsung32_encode_repeat( |  | ||||||
|     InfraredCommonEncoder* encoder, |  | ||||||
|     uint32_t* duration, |  | ||||||
|     bool* level); |  | ||||||
| const InfraredProtocolSpecification* infrared_samsung32_get_spec(InfraredProtocol protocol); |  | ||||||
| 
 |  | ||||||
| extern const InfraredCommonProtocolSpec protocol_samsung32; |  | ||||||
| 
 |  | ||||||
| /***************************************************************************************************
 |  | ||||||
| *   RC6 protocol description |  | ||||||
| *   https://www.mikrocontroller.net/articles/IRMP_-_english#RC6_.2B_RC6A
 |  | ||||||
| **************************************************************************************************** |  | ||||||
| *      Preamble                       Manchester/biphase                       Silence |  | ||||||
| *     mark/space                          Modulation |  | ||||||
| * |  | ||||||
| *    2666     889        444/888 - bit (x2 for toggle bit)                       2666 |  | ||||||
| * |  | ||||||
| *  ________         __    __  __  __    ____  __  __  __  __  __  __  __  __ |  | ||||||
| * _        _________  ____  __  __  ____    __  __  __  __  __  __  __  __  _______________ |  | ||||||
| *                   | 1 | 0 | 0 | 0 |   0   |      ...      |      ...      |             | |  | ||||||
| *                     s  m2  m1  m0     T     address (MSB)   command (MSB) |  | ||||||
| * |  | ||||||
| *    s - start bit (always 1) |  | ||||||
| *    m0-2 - mode (000 for RC6) |  | ||||||
| *    T - toggle bit, twice longer |  | ||||||
| *    address - 8 bit |  | ||||||
| *    command - 8 bit |  | ||||||
| ***************************************************************************************************/ |  | ||||||
| 
 |  | ||||||
| #define INFRARED_RC6_CARRIER_FREQUENCY 36000 |  | ||||||
| #define INFRARED_RC6_DUTY_CYCLE 0.33 |  | ||||||
| 
 |  | ||||||
| #define INFRARED_RC6_PREAMBLE_MARK 2666 |  | ||||||
| #define INFRARED_RC6_PREAMBLE_SPACE 889 |  | ||||||
| #define INFRARED_RC6_BIT 444 // half of time-quant for 1 bit
 |  | ||||||
| #define INFRARED_RC6_PREAMBLE_TOLERANCE 200 // us
 |  | ||||||
| #define INFRARED_RC6_BIT_TOLERANCE 120 // us
 |  | ||||||
| /* protocol allows 2700 silence, but it is hard to send 1 message without repeat */ |  | ||||||
| #define INFRARED_RC6_SILENCE (2700 * 10) |  | ||||||
| #define INFRARED_RC6_MIN_SPLIT_TIME 2700 |  | ||||||
| 
 |  | ||||||
| void* infrared_decoder_rc6_alloc(void); |  | ||||||
| void infrared_decoder_rc6_reset(void* decoder); |  | ||||||
| void infrared_decoder_rc6_free(void* decoder); |  | ||||||
| InfraredMessage* infrared_decoder_rc6_check_ready(void* ctx); |  | ||||||
| InfraredMessage* infrared_decoder_rc6_decode(void* decoder, bool level, uint32_t duration); |  | ||||||
| void* infrared_encoder_rc6_alloc(void); |  | ||||||
| void infrared_encoder_rc6_reset(void* encoder_ptr, const InfraredMessage* message); |  | ||||||
| void infrared_encoder_rc6_free(void* decoder); |  | ||||||
| InfraredStatus infrared_encoder_rc6_encode(void* encoder_ptr, uint32_t* duration, bool* polarity); |  | ||||||
| bool infrared_decoder_rc6_interpret(InfraredCommonDecoder* decoder); |  | ||||||
| InfraredStatus infrared_decoder_rc6_decode_manchester( |  | ||||||
|     InfraredCommonDecoder* decoder, |  | ||||||
|     bool level, |  | ||||||
|     uint32_t timing); |  | ||||||
| InfraredStatus infrared_encoder_rc6_encode_manchester( |  | ||||||
|     InfraredCommonEncoder* encoder_ptr, |  | ||||||
|     uint32_t* duration, |  | ||||||
|     bool* polarity); |  | ||||||
| const InfraredProtocolSpecification* infrared_rc6_get_spec(InfraredProtocol protocol); |  | ||||||
| 
 |  | ||||||
| extern const InfraredCommonProtocolSpec protocol_rc6; |  | ||||||
| 
 |  | ||||||
| /***************************************************************************************************
 |  | ||||||
| *   RC5 protocol description |  | ||||||
| *   https://www.mikrocontroller.net/articles/IRMP_-_english#RC5_.2B_RC5X
 |  | ||||||
| **************************************************************************************************** |  | ||||||
| *                                       Manchester/biphase |  | ||||||
| *                                           Modulation |  | ||||||
| * |  | ||||||
| *                              888/1776 - bit (x2 for toggle bit) |  | ||||||
| * |  | ||||||
| *                           __  ____    __  __  __  __  __  __  __  __ |  | ||||||
| *                         __  __    ____  __  __  __  __  __  __  __  _ |  | ||||||
| *                         | 1 | 1 | 0 |      ...      |      ...      | |  | ||||||
| *                           s  si   T   address (MSB)   command (MSB) |  | ||||||
| * |  | ||||||
| *    Note: manchester starts from space timing, so it have to be handled properly |  | ||||||
| *    s - start bit (always 1) |  | ||||||
| *    si - RC5: start bit (always 1), RC5X - 7-th bit of address (in our case always 0) |  | ||||||
| *    T - toggle bit, change it's value every button press |  | ||||||
| *    address - 5 bit |  | ||||||
| *    command - 6/7 bit |  | ||||||
| ***************************************************************************************************/ |  | ||||||
| 
 |  | ||||||
| #define INFRARED_RC5_CARRIER_FREQUENCY 36000 |  | ||||||
| #define INFRARED_RC5_DUTY_CYCLE 0.33 |  | ||||||
| 
 |  | ||||||
| #define INFRARED_RC5_PREAMBLE_MARK 0 |  | ||||||
| #define INFRARED_RC5_PREAMBLE_SPACE 0 |  | ||||||
| #define INFRARED_RC5_BIT 888 // half of time-quant for 1 bit
 |  | ||||||
| #define INFRARED_RC5_PREAMBLE_TOLERANCE 200 // us
 |  | ||||||
| #define INFRARED_RC5_BIT_TOLERANCE 120 // us
 |  | ||||||
| /* protocol allows 2700 silence, but it is hard to send 1 message without repeat */ |  | ||||||
| #define INFRARED_RC5_SILENCE (2700 * 10) |  | ||||||
| #define INFRARED_RC5_MIN_SPLIT_TIME 2700 |  | ||||||
| 
 |  | ||||||
| void* infrared_decoder_rc5_alloc(void); |  | ||||||
| void infrared_decoder_rc5_reset(void* decoder); |  | ||||||
| void infrared_decoder_rc5_free(void* decoder); |  | ||||||
| InfraredMessage* infrared_decoder_rc5_check_ready(void* ctx); |  | ||||||
| InfraredMessage* infrared_decoder_rc5_decode(void* decoder, bool level, uint32_t duration); |  | ||||||
| void* infrared_encoder_rc5_alloc(void); |  | ||||||
| void infrared_encoder_rc5_reset(void* encoder_ptr, const InfraredMessage* message); |  | ||||||
| void infrared_encoder_rc5_free(void* decoder); |  | ||||||
| InfraredStatus infrared_encoder_rc5_encode(void* encoder_ptr, uint32_t* duration, bool* polarity); |  | ||||||
| bool infrared_decoder_rc5_interpret(InfraredCommonDecoder* decoder); |  | ||||||
| const InfraredProtocolSpecification* infrared_rc5_get_spec(InfraredProtocol protocol); |  | ||||||
| 
 |  | ||||||
| extern const InfraredCommonProtocolSpec protocol_rc5; |  | ||||||
| 
 |  | ||||||
| /***************************************************************************************************
 |  | ||||||
| *   Sony SIRC protocol description |  | ||||||
| *   https://www.sbprojects.net/knowledge/ir/sirc.php
 |  | ||||||
| *   http://picprojects.org.uk/
 |  | ||||||
| **************************************************************************************************** |  | ||||||
| *      Preamble  Preamble     Pulse Width Modulation           Pause             Entirely repeat |  | ||||||
| *        mark     space                                     up to period             message.. |  | ||||||
| * |  | ||||||
| *        2400      600      12/15/20 bits (600,1200)         ...45000          2400      600 |  | ||||||
| *     __________          _ _ _ _  _  _  _ _ _  _  _ _ _                    __________          _ _ |  | ||||||
| * ____          __________ _ _ _ __ __ __ _ _ __ __ _ _ ____________________          __________ _ |  | ||||||
| *                        |    command    |   address    | |  | ||||||
| *                 SIRC   |     7b LSB    |    5b LSB    | |  | ||||||
| *                 SIRC15 |     7b LSB    |    8b LSB    | |  | ||||||
| *                 SIRC20 |     7b LSB    |    13b LSB   | |  | ||||||
| * |  | ||||||
| * No way to determine either next message is repeat or not, |  | ||||||
| * so recognize only fact message received. Sony remotes always send at least 3 messages. |  | ||||||
| * Assume 8 last extended bits for SIRC20 are address bits. |  | ||||||
| ***************************************************************************************************/ |  | ||||||
| 
 |  | ||||||
| #define INFRARED_SIRC_CARRIER_FREQUENCY 40000 |  | ||||||
| #define INFRARED_SIRC_DUTY_CYCLE 0.33 |  | ||||||
| #define INFRARED_SIRC_PREAMBLE_MARK 2400 |  | ||||||
| #define INFRARED_SIRC_PREAMBLE_SPACE 600 |  | ||||||
| #define INFRARED_SIRC_BIT1_MARK 1200 |  | ||||||
| #define INFRARED_SIRC_BIT1_SPACE 600 |  | ||||||
| #define INFRARED_SIRC_BIT0_MARK 600 |  | ||||||
| #define INFRARED_SIRC_BIT0_SPACE 600 |  | ||||||
| #define INFRARED_SIRC_PREAMBLE_TOLERANCE 200 // us
 |  | ||||||
| #define INFRARED_SIRC_BIT_TOLERANCE 120 // us
 |  | ||||||
| #define INFRARED_SIRC_SILENCE 10000 |  | ||||||
| #define INFRARED_SIRC_MIN_SPLIT_TIME (INFRARED_SIRC_SILENCE - 1000) |  | ||||||
| #define INFRARED_SIRC_REPEAT_PERIOD 45000 |  | ||||||
| 
 |  | ||||||
| void* infrared_decoder_sirc_alloc(void); |  | ||||||
| void infrared_decoder_sirc_reset(void* decoder); |  | ||||||
| InfraredMessage* infrared_decoder_sirc_check_ready(void* decoder); |  | ||||||
| uint32_t infrared_decoder_sirc_get_timeout(void* decoder); |  | ||||||
| void infrared_decoder_sirc_free(void* decoder); |  | ||||||
| InfraredMessage* infrared_decoder_sirc_decode(void* decoder, bool level, uint32_t duration); |  | ||||||
| void* infrared_encoder_sirc_alloc(void); |  | ||||||
| void infrared_encoder_sirc_reset(void* encoder_ptr, const InfraredMessage* message); |  | ||||||
| void infrared_encoder_sirc_free(void* decoder); |  | ||||||
| InfraredStatus infrared_encoder_sirc_encode(void* encoder_ptr, uint32_t* duration, bool* polarity); |  | ||||||
| bool infrared_decoder_sirc_interpret(InfraredCommonDecoder* decoder); |  | ||||||
| const InfraredProtocolSpecification* infrared_sirc_get_spec(InfraredProtocol protocol); |  | ||||||
| InfraredStatus infrared_encoder_sirc_encode_repeat( |  | ||||||
|     InfraredCommonEncoder* encoder, |  | ||||||
|     uint32_t* duration, |  | ||||||
|     bool* level); |  | ||||||
| 
 |  | ||||||
| extern const InfraredCommonProtocolSpec protocol_sirc; |  | ||||||
| 
 |  | ||||||
| /***************************************************************************************************
 |  | ||||||
| *   Kaseikyo protocol description |  | ||||||
| *   https://github.com/Arduino-IRremote/Arduino-IRremote/blob/master/src/ir_Kaseikyo.hpp
 |  | ||||||
| **************************************************************************************************** |  | ||||||
| *     Preamble   Preamble      Pulse Distance/Width          Pause       Preamble   Preamble |  | ||||||
| *       mark      space            Modulation             up to period    repeat     repeat |  | ||||||
| *                                                                          mark       space |  | ||||||
| * |  | ||||||
| *        3360      1665               48 bit              ...130000        3456       1728 |  | ||||||
| *     __________          _ _ _ _  _  _  _ _ _  _  _ _ _                ___________ |  | ||||||
| * ____          __________ _ _ _ __ __ __ _ _ __ __ _ _ ________________           ___________ |  | ||||||
| * |  | ||||||
| ***************************************************************************************************/ |  | ||||||
| 
 |  | ||||||
| #define INFRARED_KASEIKYO_UNIT 432 |  | ||||||
| #define INFRARED_KASEIKYO_PREAMBLE_MARK (8 * INFRARED_KASEIKYO_UNIT) |  | ||||||
| #define INFRARED_KASEIKYO_PREAMBLE_SPACE (4 * INFRARED_KASEIKYO_UNIT) |  | ||||||
| #define INFRARED_KASEIKYO_BIT1_MARK INFRARED_KASEIKYO_UNIT |  | ||||||
| #define INFRARED_KASEIKYO_BIT1_SPACE (3 * INFRARED_KASEIKYO_UNIT) |  | ||||||
| #define INFRARED_KASEIKYO_BIT0_MARK INFRARED_KASEIKYO_UNIT |  | ||||||
| #define INFRARED_KASEIKYO_BIT0_SPACE INFRARED_KASEIKYO_UNIT |  | ||||||
| #define INFRARED_KASEIKYO_REPEAT_PERIOD 130000 |  | ||||||
| #define INFRARED_KASEIKYO_SILENCE INFRARED_KASEIKYO_REPEAT_PERIOD |  | ||||||
| #define INFRARED_KASEIKYO_MIN_SPLIT_TIME INFRARED_KASEIKYO_REPEAT_PAUSE_MIN |  | ||||||
| #define INFRARED_KASEIKYO_REPEAT_PAUSE_MIN 4000 |  | ||||||
| #define INFRARED_KASEIKYO_REPEAT_PAUSE_MAX 150000 |  | ||||||
| #define INFRARED_KASEIKYO_REPEAT_MARK INFRARED_KASEIKYO_PREAMBLE_MARK |  | ||||||
| #define INFRARED_KASEIKYO_REPEAT_SPACE (INFRARED_KASEIKYO_REPEAT_PERIOD - 56000) |  | ||||||
| #define INFRARED_KASEIKYO_PREAMBLE_TOLERANCE 200 // us
 |  | ||||||
| #define INFRARED_KASEIKYO_BIT_TOLERANCE 120 // us
 |  | ||||||
| 
 |  | ||||||
| void* infrared_decoder_kaseikyo_alloc(void); |  | ||||||
| void infrared_decoder_kaseikyo_reset(void* decoder); |  | ||||||
| void infrared_decoder_kaseikyo_free(void* decoder); |  | ||||||
| InfraredMessage* infrared_decoder_kaseikyo_check_ready(void* decoder); |  | ||||||
| InfraredMessage* infrared_decoder_kaseikyo_decode(void* decoder, bool level, uint32_t duration); |  | ||||||
| void* infrared_encoder_kaseikyo_alloc(void); |  | ||||||
| InfraredStatus |  | ||||||
|     infrared_encoder_kaseikyo_encode(void* encoder_ptr, uint32_t* duration, bool* level); |  | ||||||
| void infrared_encoder_kaseikyo_reset(void* encoder_ptr, const InfraredMessage* message); |  | ||||||
| void infrared_encoder_kaseikyo_free(void* encoder_ptr); |  | ||||||
| bool infrared_decoder_kaseikyo_interpret(InfraredCommonDecoder* decoder); |  | ||||||
| InfraredStatus infrared_decoder_kaseikyo_decode_repeat(InfraredCommonDecoder* decoder); |  | ||||||
| InfraredStatus infrared_encoder_kaseikyo_encode_repeat( |  | ||||||
|     InfraredCommonEncoder* encoder, |  | ||||||
|     uint32_t* duration, |  | ||||||
|     bool* level); |  | ||||||
| const InfraredProtocolSpecification* infrared_kaseikyo_get_spec(InfraredProtocol protocol); |  | ||||||
| 
 |  | ||||||
| extern const InfraredCommonProtocolSpec protocol_kaseikyo; |  | ||||||
| @ -1,9 +1,5 @@ | |||||||
| #include "infrared.h" | #include "infrared_protocol_kaseikyo_i.h" | ||||||
| #include "infrared_protocol_defs_i.h" | #include <core/check.h> | ||||||
| #include <stdbool.h> |  | ||||||
| #include <stdint.h> |  | ||||||
| #include <furi.h> |  | ||||||
| #include "../infrared_i.h" |  | ||||||
| 
 | 
 | ||||||
| InfraredMessage* infrared_decoder_kaseikyo_check_ready(void* ctx) { | InfraredMessage* infrared_decoder_kaseikyo_check_ready(void* ctx) { | ||||||
|     return infrared_common_decoder_check_ready(ctx); |     return infrared_common_decoder_check_ready(ctx); | ||||||
| @ -38,7 +34,7 @@ bool infrared_decoder_kaseikyo_interpret(InfraredCommonDecoder* decoder) { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void* infrared_decoder_kaseikyo_alloc(void) { | void* infrared_decoder_kaseikyo_alloc(void) { | ||||||
|     return infrared_common_decoder_alloc(&protocol_kaseikyo); |     return infrared_common_decoder_alloc(&infrared_protocol_kaseikyo); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| InfraredMessage* infrared_decoder_kaseikyo_decode(void* decoder, bool level, uint32_t duration) { | InfraredMessage* infrared_decoder_kaseikyo_decode(void* decoder, bool level, uint32_t duration) { | ||||||
|  | |||||||
| @ -1,9 +1,5 @@ | |||||||
|  | #include "infrared_protocol_kaseikyo_i.h" | ||||||
| #include <core/check.h> | #include <core/check.h> | ||||||
| #include "common/infrared_common_i.h" |  | ||||||
| #include <stdint.h> |  | ||||||
| #include "../infrared_i.h" |  | ||||||
| #include "infrared_protocol_defs_i.h" |  | ||||||
| #include <furi.h> |  | ||||||
| 
 | 
 | ||||||
| void infrared_encoder_kaseikyo_reset(void* encoder_ptr, const InfraredMessage* message) { | void infrared_encoder_kaseikyo_reset(void* encoder_ptr, const InfraredMessage* message) { | ||||||
|     furi_assert(encoder_ptr); |     furi_assert(encoder_ptr); | ||||||
| @ -32,7 +28,7 @@ void infrared_encoder_kaseikyo_reset(void* encoder_ptr, const InfraredMessage* m | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void* infrared_encoder_kaseikyo_alloc(void) { | void* infrared_encoder_kaseikyo_alloc(void) { | ||||||
|     return infrared_common_encoder_alloc(&protocol_kaseikyo); |     return infrared_common_encoder_alloc(&infrared_protocol_kaseikyo); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void infrared_encoder_kaseikyo_free(void* encoder_ptr) { | void infrared_encoder_kaseikyo_free(void* encoder_ptr) { | ||||||
|  | |||||||
| @ -1,17 +0,0 @@ | |||||||
| #include "../infrared_i.h" |  | ||||||
| #include "infrared_protocol_defs_i.h" |  | ||||||
| 
 |  | ||||||
| static const InfraredProtocolSpecification infrared_kaseikyo_protocol_specification = { |  | ||||||
|     .name = "Kaseikyo", |  | ||||||
|     .address_length = 26, |  | ||||||
|     .command_length = 10, |  | ||||||
|     .frequency = INFRARED_COMMON_CARRIER_FREQUENCY, |  | ||||||
|     .duty_cycle = INFRARED_COMMON_DUTY_CYCLE, |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| const InfraredProtocolSpecification* infrared_kaseikyo_get_spec(InfraredProtocol protocol) { |  | ||||||
|     if(protocol == InfraredProtocolKaseikyo) |  | ||||||
|         return &infrared_kaseikyo_protocol_specification; |  | ||||||
|     else |  | ||||||
|         return NULL; |  | ||||||
| } |  | ||||||
| @ -0,0 +1,40 @@ | |||||||
|  | #include "infrared_protocol_kaseikyo_i.h" | ||||||
|  | 
 | ||||||
|  | const InfraredCommonProtocolSpec infrared_protocol_kaseikyo = { | ||||||
|  |     .timings = | ||||||
|  |         { | ||||||
|  |             .preamble_mark = INFRARED_KASEIKYO_PREAMBLE_MARK, | ||||||
|  |             .preamble_space = INFRARED_KASEIKYO_PREAMBLE_SPACE, | ||||||
|  |             .bit1_mark = INFRARED_KASEIKYO_BIT1_MARK, | ||||||
|  |             .bit1_space = INFRARED_KASEIKYO_BIT1_SPACE, | ||||||
|  |             .bit0_mark = INFRARED_KASEIKYO_BIT0_MARK, | ||||||
|  |             .bit0_space = INFRARED_KASEIKYO_BIT0_SPACE, | ||||||
|  |             .preamble_tolerance = INFRARED_KASEIKYO_PREAMBLE_TOLERANCE, | ||||||
|  |             .bit_tolerance = INFRARED_KASEIKYO_BIT_TOLERANCE, | ||||||
|  |             .silence_time = INFRARED_KASEIKYO_SILENCE, | ||||||
|  |             .min_split_time = INFRARED_KASEIKYO_MIN_SPLIT_TIME, | ||||||
|  |         }, | ||||||
|  |     .databit_len[0] = 48, | ||||||
|  |     .no_stop_bit = false, | ||||||
|  |     .decode = infrared_common_decode_pdwm, | ||||||
|  |     .encode = infrared_common_encode_pdwm, | ||||||
|  |     .interpret = infrared_decoder_kaseikyo_interpret, | ||||||
|  |     .decode_repeat = NULL, | ||||||
|  |     .encode_repeat = NULL, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | static const InfraredProtocolVariant infrared_protocol_variant_kaseikyo = { | ||||||
|  |     .name = "Kaseikyo", | ||||||
|  |     .address_length = 26, | ||||||
|  |     .command_length = 10, | ||||||
|  |     .frequency = INFRARED_COMMON_CARRIER_FREQUENCY, | ||||||
|  |     .duty_cycle = INFRARED_COMMON_DUTY_CYCLE, | ||||||
|  |     .repeat_count = INFRARED_KASEIKYO_REPEAT_COUNT_MIN, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | const InfraredProtocolVariant* infrared_protocol_kaseikyo_get_variant(InfraredProtocol protocol) { | ||||||
|  |     if(protocol == InfraredProtocolKaseikyo) | ||||||
|  |         return &infrared_protocol_variant_kaseikyo; | ||||||
|  |     else | ||||||
|  |         return NULL; | ||||||
|  | } | ||||||
| @ -0,0 +1,31 @@ | |||||||
|  | #pragma once | ||||||
|  | 
 | ||||||
|  | #include "../infrared_i.h" | ||||||
|  | 
 | ||||||
|  | /***************************************************************************************************
 | ||||||
|  | *   Kaseikyo protocol description | ||||||
|  | *   https://github.com/Arduino-IRremote/Arduino-IRremote/blob/master/src/ir_Kaseikyo.hpp
 | ||||||
|  | **************************************************************************************************** | ||||||
|  | *     Preamble   Preamble      Pulse Distance/Width          Pause       Preamble   Preamble | ||||||
|  | *       mark      space            Modulation             up to period    repeat     repeat | ||||||
|  | *                                                                          mark       space | ||||||
|  | * | ||||||
|  | *        3360      1665               48 bit              ...130000        3456       1728 | ||||||
|  | *     __________          _ _ _ _  _  _  _ _ _  _  _ _ _                ___________ | ||||||
|  | * ____          __________ _ _ _ __ __ __ _ _ __ __ _ _ ________________           ___________ | ||||||
|  | * | ||||||
|  | ***************************************************************************************************/ | ||||||
|  | 
 | ||||||
|  | void* infrared_decoder_kaseikyo_alloc(void); | ||||||
|  | void infrared_decoder_kaseikyo_reset(void* decoder); | ||||||
|  | void infrared_decoder_kaseikyo_free(void* decoder); | ||||||
|  | InfraredMessage* infrared_decoder_kaseikyo_check_ready(void* decoder); | ||||||
|  | InfraredMessage* infrared_decoder_kaseikyo_decode(void* decoder, bool level, uint32_t duration); | ||||||
|  | 
 | ||||||
|  | void* infrared_encoder_kaseikyo_alloc(void); | ||||||
|  | InfraredStatus | ||||||
|  |     infrared_encoder_kaseikyo_encode(void* encoder_ptr, uint32_t* duration, bool* level); | ||||||
|  | void infrared_encoder_kaseikyo_reset(void* encoder_ptr, const InfraredMessage* message); | ||||||
|  | void infrared_encoder_kaseikyo_free(void* encoder_ptr); | ||||||
|  | 
 | ||||||
|  | const InfraredProtocolVariant* infrared_protocol_kaseikyo_get_variant(InfraredProtocol protocol); | ||||||
| @ -0,0 +1,30 @@ | |||||||
|  | #pragma once | ||||||
|  | 
 | ||||||
|  | #include "../common/infrared_common_i.h" | ||||||
|  | 
 | ||||||
|  | #define INFRARED_KASEIKYO_UNIT 432 | ||||||
|  | #define INFRARED_KASEIKYO_PREAMBLE_MARK (8 * INFRARED_KASEIKYO_UNIT) | ||||||
|  | #define INFRARED_KASEIKYO_PREAMBLE_SPACE (4 * INFRARED_KASEIKYO_UNIT) | ||||||
|  | #define INFRARED_KASEIKYO_BIT1_MARK INFRARED_KASEIKYO_UNIT | ||||||
|  | #define INFRARED_KASEIKYO_BIT1_SPACE (3 * INFRARED_KASEIKYO_UNIT) | ||||||
|  | #define INFRARED_KASEIKYO_BIT0_MARK INFRARED_KASEIKYO_UNIT | ||||||
|  | #define INFRARED_KASEIKYO_BIT0_SPACE INFRARED_KASEIKYO_UNIT | ||||||
|  | #define INFRARED_KASEIKYO_REPEAT_PERIOD 130000 | ||||||
|  | #define INFRARED_KASEIKYO_SILENCE INFRARED_KASEIKYO_REPEAT_PERIOD | ||||||
|  | #define INFRARED_KASEIKYO_MIN_SPLIT_TIME INFRARED_KASEIKYO_REPEAT_PAUSE_MIN | ||||||
|  | #define INFRARED_KASEIKYO_REPEAT_PAUSE_MIN 4000 | ||||||
|  | #define INFRARED_KASEIKYO_REPEAT_PAUSE_MAX 150000 | ||||||
|  | #define INFRARED_KASEIKYO_REPEAT_COUNT_MIN 1 | ||||||
|  | #define INFRARED_KASEIKYO_REPEAT_MARK INFRARED_KASEIKYO_PREAMBLE_MARK | ||||||
|  | #define INFRARED_KASEIKYO_REPEAT_SPACE (INFRARED_KASEIKYO_REPEAT_PERIOD - 56000) | ||||||
|  | #define INFRARED_KASEIKYO_PREAMBLE_TOLERANCE 200 // us
 | ||||||
|  | #define INFRARED_KASEIKYO_BIT_TOLERANCE 120 // us
 | ||||||
|  | 
 | ||||||
|  | extern const InfraredCommonProtocolSpec infrared_protocol_kaseikyo; | ||||||
|  | 
 | ||||||
|  | bool infrared_decoder_kaseikyo_interpret(InfraredCommonDecoder* decoder); | ||||||
|  | InfraredStatus infrared_decoder_kaseikyo_decode_repeat(InfraredCommonDecoder* decoder); | ||||||
|  | InfraredStatus infrared_encoder_kaseikyo_encode_repeat( | ||||||
|  |     InfraredCommonEncoder* encoder, | ||||||
|  |     uint32_t* duration, | ||||||
|  |     bool* level); | ||||||
| @ -1,10 +1,5 @@ | |||||||
| #include "common/infrared_common_i.h" | #include "infrared_protocol_nec_i.h" | ||||||
| #include "infrared.h" | #include <core/check.h> | ||||||
| #include "infrared_protocol_defs_i.h" |  | ||||||
| #include <stdbool.h> |  | ||||||
| #include <stdint.h> |  | ||||||
| #include <furi.h> |  | ||||||
| #include "../infrared_i.h" |  | ||||||
| 
 | 
 | ||||||
| InfraredMessage* infrared_decoder_nec_check_ready(void* ctx) { | InfraredMessage* infrared_decoder_nec_check_ready(void* ctx) { | ||||||
|     return infrared_common_decoder_check_ready(ctx); |     return infrared_common_decoder_check_ready(ctx); | ||||||
| @ -86,7 +81,7 @@ InfraredStatus infrared_decoder_nec_decode_repeat(InfraredCommonDecoder* decoder | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void* infrared_decoder_nec_alloc(void) { | void* infrared_decoder_nec_alloc(void) { | ||||||
|     return infrared_common_decoder_alloc(&protocol_nec); |     return infrared_common_decoder_alloc(&infrared_protocol_nec); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| InfraredMessage* infrared_decoder_nec_decode(void* decoder, bool level, uint32_t duration) { | InfraredMessage* infrared_decoder_nec_decode(void* decoder, bool level, uint32_t duration) { | ||||||
|  | |||||||
| @ -1,10 +1,7 @@ | |||||||
|  | #include "infrared_protocol_nec_i.h" | ||||||
|  | 
 | ||||||
|  | #include <core/core_defines.h> | ||||||
| #include <core/check.h> | #include <core/check.h> | ||||||
| #include "infrared.h" |  | ||||||
| #include "common/infrared_common_i.h" |  | ||||||
| #include <stdint.h> |  | ||||||
| #include "../infrared_i.h" |  | ||||||
| #include "infrared_protocol_defs_i.h" |  | ||||||
| #include <furi.h> |  | ||||||
| 
 | 
 | ||||||
| static const uint32_t repeat_timings[] = { | static const uint32_t repeat_timings[] = { | ||||||
|     INFRARED_NEC_REPEAT_PERIOD - INFRARED_NEC_REPEAT_MARK - INFRARED_NEC_REPEAT_SPACE - |     INFRARED_NEC_REPEAT_PERIOD - INFRARED_NEC_REPEAT_MARK - INFRARED_NEC_REPEAT_SPACE - | ||||||
| @ -81,7 +78,7 @@ InfraredStatus infrared_encoder_nec_encode_repeat( | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void* infrared_encoder_nec_alloc(void) { | void* infrared_encoder_nec_alloc(void) { | ||||||
|     return infrared_common_encoder_alloc(&protocol_nec); |     return infrared_common_encoder_alloc(&infrared_protocol_nec); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void infrared_encoder_nec_free(void* encoder_ptr) { | void infrared_encoder_nec_free(void* encoder_ptr) { | ||||||
|  | |||||||
| @ -1,47 +0,0 @@ | |||||||
| #include "../infrared_i.h" |  | ||||||
| #include "infrared_protocol_defs_i.h" |  | ||||||
| 
 |  | ||||||
| static const InfraredProtocolSpecification infrared_nec_protocol_specification = { |  | ||||||
|     .name = "NEC", |  | ||||||
|     .address_length = 8, |  | ||||||
|     .command_length = 8, |  | ||||||
|     .frequency = INFRARED_COMMON_CARRIER_FREQUENCY, |  | ||||||
|     .duty_cycle = INFRARED_COMMON_DUTY_CYCLE, |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| static const InfraredProtocolSpecification infrared_necext_protocol_specification = { |  | ||||||
|     .name = "NECext", |  | ||||||
|     .address_length = 16, |  | ||||||
|     .command_length = 16, |  | ||||||
|     .frequency = INFRARED_COMMON_CARRIER_FREQUENCY, |  | ||||||
|     .duty_cycle = INFRARED_COMMON_DUTY_CYCLE, |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| static const InfraredProtocolSpecification infrared_nec42_protocol_specification = { |  | ||||||
|     .name = "NEC42", |  | ||||||
|     .address_length = 13, |  | ||||||
|     .command_length = 8, |  | ||||||
|     .frequency = INFRARED_COMMON_CARRIER_FREQUENCY, |  | ||||||
|     .duty_cycle = INFRARED_COMMON_DUTY_CYCLE, |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| static const InfraredProtocolSpecification infrared_nec42ext_protocol_specification = { |  | ||||||
|     .name = "NEC42ext", |  | ||||||
|     .address_length = 26, |  | ||||||
|     .command_length = 16, |  | ||||||
|     .frequency = INFRARED_COMMON_CARRIER_FREQUENCY, |  | ||||||
|     .duty_cycle = INFRARED_COMMON_DUTY_CYCLE, |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| const InfraredProtocolSpecification* infrared_nec_get_spec(InfraredProtocol protocol) { |  | ||||||
|     if(protocol == InfraredProtocolNEC) |  | ||||||
|         return &infrared_nec_protocol_specification; |  | ||||||
|     else if(protocol == InfraredProtocolNECext) |  | ||||||
|         return &infrared_necext_protocol_specification; |  | ||||||
|     else if(protocol == InfraredProtocolNEC42) |  | ||||||
|         return &infrared_nec42_protocol_specification; |  | ||||||
|     else if(protocol == InfraredProtocolNEC42ext) |  | ||||||
|         return &infrared_nec42ext_protocol_specification; |  | ||||||
|     else |  | ||||||
|         return NULL; |  | ||||||
| } |  | ||||||
							
								
								
									
										74
									
								
								lib/infrared/encoder_decoder/nec/infrared_protocol_nec.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @ -0,0 +1,74 @@ | |||||||
|  | #include "infrared_protocol_nec_i.h" | ||||||
|  | 
 | ||||||
|  | const InfraredCommonProtocolSpec infrared_protocol_nec = { | ||||||
|  |     .timings = | ||||||
|  |         { | ||||||
|  |             .preamble_mark = INFRARED_NEC_PREAMBLE_MARK, | ||||||
|  |             .preamble_space = INFRARED_NEC_PREAMBLE_SPACE, | ||||||
|  |             .bit1_mark = INFRARED_NEC_BIT1_MARK, | ||||||
|  |             .bit1_space = INFRARED_NEC_BIT1_SPACE, | ||||||
|  |             .bit0_mark = INFRARED_NEC_BIT0_MARK, | ||||||
|  |             .bit0_space = INFRARED_NEC_BIT0_SPACE, | ||||||
|  |             .preamble_tolerance = INFRARED_NEC_PREAMBLE_TOLERANCE, | ||||||
|  |             .bit_tolerance = INFRARED_NEC_BIT_TOLERANCE, | ||||||
|  |             .silence_time = INFRARED_NEC_SILENCE, | ||||||
|  |             .min_split_time = INFRARED_NEC_MIN_SPLIT_TIME, | ||||||
|  |         }, | ||||||
|  |     .databit_len[0] = 42, | ||||||
|  |     .databit_len[1] = 32, | ||||||
|  |     .no_stop_bit = false, | ||||||
|  |     .decode = infrared_common_decode_pdwm, | ||||||
|  |     .encode = infrared_common_encode_pdwm, | ||||||
|  |     .interpret = infrared_decoder_nec_interpret, | ||||||
|  |     .decode_repeat = infrared_decoder_nec_decode_repeat, | ||||||
|  |     .encode_repeat = infrared_encoder_nec_encode_repeat, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | static const InfraredProtocolVariant infrared_protocol_variant_nec = { | ||||||
|  |     .name = "NEC", | ||||||
|  |     .address_length = 8, | ||||||
|  |     .command_length = 8, | ||||||
|  |     .frequency = INFRARED_COMMON_CARRIER_FREQUENCY, | ||||||
|  |     .duty_cycle = INFRARED_COMMON_DUTY_CYCLE, | ||||||
|  |     .repeat_count = INFRARED_NEC_REPEAT_COUNT_MIN, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | static const InfraredProtocolVariant infrared_protocol_variant_necext = { | ||||||
|  |     .name = "NECext", | ||||||
|  |     .address_length = 16, | ||||||
|  |     .command_length = 16, | ||||||
|  |     .frequency = INFRARED_COMMON_CARRIER_FREQUENCY, | ||||||
|  |     .duty_cycle = INFRARED_COMMON_DUTY_CYCLE, | ||||||
|  |     .repeat_count = INFRARED_NEC_REPEAT_COUNT_MIN, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | static const InfraredProtocolVariant infrared_protocol_variant_nec42 = { | ||||||
|  |     .name = "NEC42", | ||||||
|  |     .address_length = 13, | ||||||
|  |     .command_length = 8, | ||||||
|  |     .frequency = INFRARED_COMMON_CARRIER_FREQUENCY, | ||||||
|  |     .duty_cycle = INFRARED_COMMON_DUTY_CYCLE, | ||||||
|  |     .repeat_count = INFRARED_NEC_REPEAT_COUNT_MIN, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | static const InfraredProtocolVariant infrared_protocol_variant_nec42ext = { | ||||||
|  |     .name = "NEC42ext", | ||||||
|  |     .address_length = 26, | ||||||
|  |     .command_length = 16, | ||||||
|  |     .frequency = INFRARED_COMMON_CARRIER_FREQUENCY, | ||||||
|  |     .duty_cycle = INFRARED_COMMON_DUTY_CYCLE, | ||||||
|  |     .repeat_count = INFRARED_NEC_REPEAT_COUNT_MIN, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | const InfraredProtocolVariant* infrared_protocol_nec_get_variant(InfraredProtocol protocol) { | ||||||
|  |     if(protocol == InfraredProtocolNEC) | ||||||
|  |         return &infrared_protocol_variant_nec; | ||||||
|  |     else if(protocol == InfraredProtocolNECext) | ||||||
|  |         return &infrared_protocol_variant_necext; | ||||||
|  |     else if(protocol == InfraredProtocolNEC42) | ||||||
|  |         return &infrared_protocol_variant_nec42; | ||||||
|  |     else if(protocol == InfraredProtocolNEC42ext) | ||||||
|  |         return &infrared_protocol_variant_nec42ext; | ||||||
|  |     else | ||||||
|  |         return NULL; | ||||||
|  | } | ||||||
							
								
								
									
										30
									
								
								lib/infrared/encoder_decoder/nec/infrared_protocol_nec.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @ -0,0 +1,30 @@ | |||||||
|  | #pragma once | ||||||
|  | 
 | ||||||
|  | #include "../infrared_i.h" | ||||||
|  | 
 | ||||||
|  | /***************************************************************************************************
 | ||||||
|  | *   NEC protocol description | ||||||
|  | *   https://radioparty.ru/manuals/encyclopedia/213-ircontrol?start=1
 | ||||||
|  | **************************************************************************************************** | ||||||
|  | *     Preamble   Preamble      Pulse Distance/Width          Pause       Preamble   Preamble  Stop | ||||||
|  | *       mark      space            Modulation             up to period    repeat     repeat    bit | ||||||
|  | *                                                                          mark       space | ||||||
|  | * | ||||||
|  | *        9000      4500         32 bit + stop bit         ...110000         9000       2250 | ||||||
|  | *     __________          _ _ _ _  _  _  _ _ _  _  _ _ _                ___________            _ | ||||||
|  | * ____          __________ _ _ _ __ __ __ _ _ __ __ _ _ ________________           ____________ ___ | ||||||
|  | * | ||||||
|  | ***************************************************************************************************/ | ||||||
|  | 
 | ||||||
|  | void* infrared_decoder_nec_alloc(void); | ||||||
|  | void infrared_decoder_nec_reset(void* decoder); | ||||||
|  | void infrared_decoder_nec_free(void* decoder); | ||||||
|  | InfraredMessage* infrared_decoder_nec_check_ready(void* decoder); | ||||||
|  | InfraredMessage* infrared_decoder_nec_decode(void* decoder, bool level, uint32_t duration); | ||||||
|  | 
 | ||||||
|  | void* infrared_encoder_nec_alloc(void); | ||||||
|  | InfraredStatus infrared_encoder_nec_encode(void* encoder_ptr, uint32_t* duration, bool* level); | ||||||
|  | void infrared_encoder_nec_reset(void* encoder_ptr, const InfraredMessage* message); | ||||||
|  | void infrared_encoder_nec_free(void* encoder_ptr); | ||||||
|  | 
 | ||||||
|  | const InfraredProtocolVariant* infrared_protocol_nec_get_variant(InfraredProtocol protocol); | ||||||
							
								
								
									
										29
									
								
								lib/infrared/encoder_decoder/nec/infrared_protocol_nec_i.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @ -0,0 +1,29 @@ | |||||||
|  | #pragma once | ||||||
|  | 
 | ||||||
|  | #include "../common/infrared_common_i.h" | ||||||
|  | 
 | ||||||
|  | #define INFRARED_NEC_PREAMBLE_MARK 9000 | ||||||
|  | #define INFRARED_NEC_PREAMBLE_SPACE 4500 | ||||||
|  | #define INFRARED_NEC_BIT1_MARK 560 | ||||||
|  | #define INFRARED_NEC_BIT1_SPACE 1690 | ||||||
|  | #define INFRARED_NEC_BIT0_MARK 560 | ||||||
|  | #define INFRARED_NEC_BIT0_SPACE 560 | ||||||
|  | #define INFRARED_NEC_REPEAT_PERIOD 110000 | ||||||
|  | #define INFRARED_NEC_SILENCE INFRARED_NEC_REPEAT_PERIOD | ||||||
|  | #define INFRARED_NEC_MIN_SPLIT_TIME INFRARED_NEC_REPEAT_PAUSE_MIN | ||||||
|  | #define INFRARED_NEC_REPEAT_PAUSE_MIN 4000 | ||||||
|  | #define INFRARED_NEC_REPEAT_PAUSE_MAX 150000 | ||||||
|  | #define INFRARED_NEC_REPEAT_COUNT_MIN 1 | ||||||
|  | #define INFRARED_NEC_REPEAT_MARK 9000 | ||||||
|  | #define INFRARED_NEC_REPEAT_SPACE 2250 | ||||||
|  | #define INFRARED_NEC_PREAMBLE_TOLERANCE 200 // us
 | ||||||
|  | #define INFRARED_NEC_BIT_TOLERANCE 120 // us
 | ||||||
|  | 
 | ||||||
|  | extern const InfraredCommonProtocolSpec infrared_protocol_nec; | ||||||
|  | 
 | ||||||
|  | bool infrared_decoder_nec_interpret(InfraredCommonDecoder* decoder); | ||||||
|  | InfraredStatus infrared_decoder_nec_decode_repeat(InfraredCommonDecoder* decoder); | ||||||
|  | InfraredStatus infrared_encoder_nec_encode_repeat( | ||||||
|  |     InfraredCommonEncoder* encoder, | ||||||
|  |     uint32_t* duration, | ||||||
|  |     bool* level); | ||||||
| @ -1,10 +1,7 @@ | |||||||
| #include "infrared.h" | #include "infrared_protocol_rc5_i.h" | ||||||
| #include <stdbool.h> | 
 | ||||||
| #include <stddef.h> | #include <stdlib.h> | ||||||
| #include <stdint.h> | #include <core/check.h> | ||||||
| #include <furi.h> |  | ||||||
| #include "../infrared_i.h" |  | ||||||
| #include "../infrared_protocol_defs_i.h" |  | ||||||
| 
 | 
 | ||||||
| typedef struct { | typedef struct { | ||||||
|     InfraredCommonDecoder* common_decoder; |     InfraredCommonDecoder* common_decoder; | ||||||
| @ -60,7 +57,7 @@ bool infrared_decoder_rc5_interpret(InfraredCommonDecoder* decoder) { | |||||||
| void* infrared_decoder_rc5_alloc(void) { | void* infrared_decoder_rc5_alloc(void) { | ||||||
|     InfraredRc5Decoder* decoder = malloc(sizeof(InfraredRc5Decoder)); |     InfraredRc5Decoder* decoder = malloc(sizeof(InfraredRc5Decoder)); | ||||||
|     decoder->toggle = false; |     decoder->toggle = false; | ||||||
|     decoder->common_decoder = infrared_common_decoder_alloc(&protocol_rc5); |     decoder->common_decoder = infrared_common_decoder_alloc(&infrared_protocol_rc5); | ||||||
|     decoder->common_decoder->context = decoder; |     decoder->common_decoder->context = decoder; | ||||||
|     return decoder; |     return decoder; | ||||||
| } | } | ||||||
|  | |||||||
| @ -1,9 +1,7 @@ | |||||||
| #include <core/memmgr.h> | #include "infrared_protocol_rc5_i.h" | ||||||
| #include "infrared.h" | 
 | ||||||
| #include "common/infrared_common_i.h" | #include <stdlib.h> | ||||||
| #include "infrared_protocol_defs_i.h" | #include <core/check.h> | ||||||
| #include <stdint.h> |  | ||||||
| #include "../infrared_i.h" |  | ||||||
| 
 | 
 | ||||||
| typedef struct InfraredEncoderRC5 { | typedef struct InfraredEncoderRC5 { | ||||||
|     InfraredCommonEncoder* common_encoder; |     InfraredCommonEncoder* common_encoder; | ||||||
| @ -41,7 +39,7 @@ InfraredStatus infrared_encoder_rc5_encode(void* encoder_ptr, uint32_t* duration | |||||||
| 
 | 
 | ||||||
| void* infrared_encoder_rc5_alloc(void) { | void* infrared_encoder_rc5_alloc(void) { | ||||||
|     InfraredEncoderRC5* encoder = malloc(sizeof(InfraredEncoderRC5)); |     InfraredEncoderRC5* encoder = malloc(sizeof(InfraredEncoderRC5)); | ||||||
|     encoder->common_encoder = infrared_common_encoder_alloc(&protocol_rc5); |     encoder->common_encoder = infrared_common_encoder_alloc(&infrared_protocol_rc5); | ||||||
|     encoder->toggle_bit = false; |     encoder->toggle_bit = false; | ||||||
|     return encoder; |     return encoder; | ||||||
| } | } | ||||||
|  | |||||||
							
								
								
									
										49
									
								
								lib/infrared/encoder_decoder/rc5/infrared_protocol_rc5.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @ -0,0 +1,49 @@ | |||||||
|  | #include "infrared_protocol_rc5_i.h" | ||||||
|  | 
 | ||||||
|  | const InfraredCommonProtocolSpec infrared_protocol_rc5 = { | ||||||
|  |     .timings = | ||||||
|  |         { | ||||||
|  |             .preamble_mark = 0, | ||||||
|  |             .preamble_space = 0, | ||||||
|  |             .bit1_mark = INFRARED_RC5_BIT, | ||||||
|  |             .preamble_tolerance = 0, | ||||||
|  |             .bit_tolerance = INFRARED_RC5_BIT_TOLERANCE, | ||||||
|  |             .silence_time = INFRARED_RC5_SILENCE, | ||||||
|  |             .min_split_time = INFRARED_RC5_MIN_SPLIT_TIME, | ||||||
|  |         }, | ||||||
|  |     .databit_len[0] = 1 + 1 + 1 + 5 + | ||||||
|  |                       6, // start_bit + start_bit/command_bit + toggle_bit + 5 address + 6 command
 | ||||||
|  |     .manchester_start_from_space = true, | ||||||
|  |     .decode = infrared_common_decode_manchester, | ||||||
|  |     .encode = infrared_common_encode_manchester, | ||||||
|  |     .interpret = infrared_decoder_rc5_interpret, | ||||||
|  |     .decode_repeat = NULL, | ||||||
|  |     .encode_repeat = NULL, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | static const InfraredProtocolVariant infrared_protocol_variant_rc5 = { | ||||||
|  |     .name = "RC5", | ||||||
|  |     .address_length = 5, | ||||||
|  |     .command_length = 6, | ||||||
|  |     .frequency = INFRARED_RC5_CARRIER_FREQUENCY, | ||||||
|  |     .duty_cycle = INFRARED_RC5_DUTY_CYCLE, | ||||||
|  |     .repeat_count = INFRARED_RC5_REPEAT_COUNT_MIN, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | static const InfraredProtocolVariant infrared_protocol_variant_rc5x = { | ||||||
|  |     .name = "RC5X", | ||||||
|  |     .address_length = 5, | ||||||
|  |     .command_length = 7, | ||||||
|  |     .frequency = INFRARED_RC5_CARRIER_FREQUENCY, | ||||||
|  |     .duty_cycle = INFRARED_RC5_DUTY_CYCLE, | ||||||
|  |     .repeat_count = INFRARED_RC5_REPEAT_COUNT_MIN, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | const InfraredProtocolVariant* infrared_protocol_rc5_get_variant(InfraredProtocol protocol) { | ||||||
|  |     if(protocol == InfraredProtocolRC5) | ||||||
|  |         return &infrared_protocol_variant_rc5; | ||||||
|  |     else if(protocol == InfraredProtocolRC5X) | ||||||
|  |         return &infrared_protocol_variant_rc5x; | ||||||
|  |     else | ||||||
|  |         return NULL; | ||||||
|  | } | ||||||
 Aleksandr Kutuzov
						Aleksandr Kutuzov