Merge remote-tracking branch 'origin/dev' into release-candidate
							
								
								
									
										27
									
								
								.github/workflows/pvs_studio.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						| @ -43,36 +43,15 @@ jobs: | ||||
|           fi | ||||
|           python3 scripts/get_env.py "--event_file=${{ github.event_path }}" "--type=$TYPE" | ||||
| 
 | ||||
|       - name: 'Make reports directory' | ||||
|       - name: 'Supply PVS credentials' | ||||
|         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 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' | ||||
|         id: pvs-warn | ||||
|         run: | | ||||
|           WARNINGS=0 | ||||
|           plog-converter \ | ||||
|               -a GA:1,2,3 \ | ||||
|               -t fullhtml \ | ||||
|               --indicate-warnings \ | ||||
|               PVS-Studio.log \ | ||||
|               -o reports/${DEFAULT_TARGET}-${SUFFIX} || WARNINGS=1 | ||||
|           ./fbt COMPACT=1 PVSNOBROWSER=1 firmware_pvs || WARNINGS=1 | ||||
|           echo "warnings=${WARNINGS}" >> $GITHUB_OUTPUT | ||||
| 
 | ||||
|       - name: 'Upload artifacts to update server' | ||||
| @ -84,7 +63,7 @@ jobs: | ||||
|           chmod 600 ./deploy_key; | ||||
|           rsync -avrzP --mkpath \ | ||||
|               -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; | ||||
| 
 | ||||
|       - name: 'Find Previous Comment' | ||||
|  | ||||
							
								
								
									
										1
									
								
								.github/workflows/unit_tests.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						| @ -56,6 +56,7 @@ jobs: | ||||
|       - name: 'Run units and validate results' | ||||
|         id: run_units | ||||
|         if: steps.copy.outcome == 'success' | ||||
|         timeout-minutes: 2.5 | ||||
|         run: | | ||||
|           source scripts/toolchain/fbtenv.sh | ||||
|           python3 scripts/testing/units.py ${{steps.device.outputs.flipper}} | ||||
|  | ||||
							
								
								
									
										7
									
								
								.github/workflows/updater_test.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						| @ -10,7 +10,7 @@ env: | ||||
| 
 | ||||
| jobs: | ||||
|   test_updater_on_bench: | ||||
|     runs-on: [self-hosted, FlipperZeroTest] # currently on same bench as units, needs different bench | ||||
|     runs-on: [self-hosted, FlipperZeroTestMac1] | ||||
|     steps: | ||||
|       - name: 'Decontaminate previous build leftovers' | ||||
|         run: | | ||||
| @ -27,7 +27,8 @@ jobs: | ||||
|       - name: 'Get flipper from device manager (mock)' | ||||
|         id: device | ||||
|         run: | | ||||
|           echo "flipper=/dev/ttyACM0" >> $GITHUB_OUTPUT | ||||
|           echo "flipper=/dev/tty.usbmodemflip_Rekigyn1" >> $GITHUB_OUTPUT | ||||
|           echo "stlink=0F020D026415303030303032" >> $GITHUB_OUTPUT | ||||
| 
 | ||||
|       - name: 'Flashing target firmware' | ||||
|         id: first_full_flash | ||||
| @ -67,7 +68,7 @@ jobs: | ||||
|       - name: 'Flash last release' | ||||
|         if: failure() | ||||
|         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' | ||||
|         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 | ||||
|  | ||||
							
								
								
									
										18
									
								
								.vscode/example/tasks.json
									
									
									
									
										vendored
									
									
								
							
							
						
						| @ -105,6 +105,12 @@ | ||||
|             "type": "shell", | ||||
|             "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", | ||||
|             "group": "build", | ||||
| @ -138,6 +144,18 @@ | ||||
|                 "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 | ||||
|             "label": "Serial Console", | ||||
|  | ||||
| @ -52,7 +52,7 @@ Almost everything in flipper firmware is built around this concept. | ||||
| 
 | ||||
| ## Naming | ||||
| 
 | ||||
| ### Type names are CamelCase | ||||
| ### Type names are PascalCase | ||||
| 
 | ||||
| Examples: | ||||
| 
 | ||||
|  | ||||
							
								
								
									
										15
									
								
								SConstruct
									
									
									
									
									
								
							
							
						
						| @ -148,9 +148,12 @@ fap_dist = [ | ||||
|             for app_artifact in firmware_env["FW_EXTAPPS"].applications.values() | ||||
|         ), | ||||
|     ), | ||||
|     *( | ||||
|         distenv.Install( | ||||
|         f"#/dist/{dist_dir}/apps", | ||||
|         "#/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( | ||||
| @ -165,6 +168,14 @@ Alias("fap_dist", fap_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 | ||||
| copro_dist = distenv.CoproBuilder( | ||||
|  | ||||
| @ -31,7 +31,8 @@ void AccessorApp::run(void) { | ||||
|     onewire_host_stop(onewire_host); | ||||
| } | ||||
| 
 | ||||
| AccessorApp::AccessorApp() { | ||||
| AccessorApp::AccessorApp() | ||||
|     : text_store{0} { | ||||
|     notification = static_cast<NotificationApp*>(furi_record_open(RECORD_NOTIFICATION)); | ||||
|     onewire_host = onewire_host_alloc(); | ||||
|     furi_hal_power_enable_otg(); | ||||
|  | ||||
| @ -171,9 +171,6 @@ bool WIEGAND::DoWiegandConversion() { | ||||
|                     return true; | ||||
|                 } else { | ||||
|                     _lastWiegand = sysTick; | ||||
|                     _bitCount = 0; | ||||
|                     _cardTemp = 0; | ||||
|                     _cardTempHigh = 0; | ||||
|                     return false; | ||||
|                 } | ||||
| 
 | ||||
|  | ||||
| @ -11,4 +11,5 @@ App( | ||||
|     stack_size=1 * 1024, | ||||
|     order=130, | ||||
|     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* app = malloc(sizeof(BtDebugApp)); | ||||
| 
 | ||||
|     // Load settings
 | ||||
|     bt_settings_load(&app->settings); | ||||
| 
 | ||||
|     // 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(); | ||||
|     // Was bt active?
 | ||||
|     const bool was_active = furi_hal_bt_is_active(); | ||||
|     // Stop advertising
 | ||||
|     furi_hal_bt_stop_advertising(); | ||||
| 
 | ||||
|     view_dispatcher_run(app->view_dispatcher); | ||||
| 
 | ||||
|     // Restart advertising
 | ||||
|     if(app->settings.enabled) { | ||||
|     if(was_active) { | ||||
|         furi_hal_bt_start_advertising(); | ||||
|     } | ||||
|     bt_debug_app_free(app); | ||||
|  | ||||
| @ -4,15 +4,14 @@ | ||||
| #include <gui/gui.h> | ||||
| #include <gui/view.h> | ||||
| #include <gui/view_dispatcher.h> | ||||
| #include <gui/modules/submenu.h> | ||||
| 
 | ||||
| #include <dialogs/dialogs.h> | ||||
| 
 | ||||
| #include <gui/modules/submenu.h> | ||||
| #include "views/bt_carrier_test.h" | ||||
| #include "views/bt_packet_test.h" | ||||
| #include <bt/bt_settings.h> | ||||
| 
 | ||||
| typedef struct { | ||||
|     BtSettings settings; | ||||
|     Gui* gui; | ||||
|     ViewDispatcher* view_dispatcher; | ||||
|     Submenu* submenu; | ||||
|  | ||||
| @ -2,8 +2,11 @@ | ||||
| 
 | ||||
| #include <gui/canvas.h> | ||||
| #include <gui/elements.h> | ||||
| 
 | ||||
| #include <lib/toolbox/float_tools.h> | ||||
| #include <m-array.h> | ||||
| #include <furi.h> | ||||
| #include <inttypes.h> | ||||
| #include <stdint.h> | ||||
| 
 | ||||
| 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)); | ||||
|     canvas_draw_str(canvas, 6, 60, model->message); | ||||
|     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); | ||||
|             canvas_draw_str_aligned(canvas, 124, 60, AlignRight, AlignBottom, info_str); | ||||
|         } | ||||
|     } else if(model->state == BtTestStateStopped) { | ||||
|         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); | ||||
|         } 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); | ||||
|         } | ||||
|     } | ||||
| @ -153,7 +156,7 @@ static bool bt_test_input_callback(InputEvent* event, void* context) { | ||||
| } | ||||
| 
 | ||||
| void bt_test_process_up(BtTest* bt_test) { | ||||
|     with_view_model( | ||||
|     with_view_model( // -V658
 | ||||
|         bt_test->view, | ||||
|         BtTestModel * model, | ||||
|         { | ||||
|  | ||||
| @ -5,6 +5,7 @@ App( | ||||
|     entry_point="display_test_app", | ||||
|     cdefines=["APP_DISPLAY_TEST"], | ||||
|     requires=["gui"], | ||||
|     fap_libs=["misc"], | ||||
|     stack_size=1 * 1024, | ||||
|     order=120, | ||||
|     fap_category="Debug", | ||||
|  | ||||
| @ -91,7 +91,6 @@ static void display_test_reload_config(DisplayTest* instance) { | ||||
|         instance->config_contrast, | ||||
|         instance->config_regulation_ratio, | ||||
|         instance->config_bias); | ||||
|     gui_update(instance->gui); | ||||
| } | ||||
| 
 | ||||
| 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_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( | ||||
|         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.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); | ||||
|             consumed = true; | ||||
|         } | ||||
|  | ||||
| @ -466,6 +466,10 @@ static void mf_classic_generator_test(uint8_t uid_len, MfClassicType type) { | ||||
|     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) { | ||||
|     mf_classic_generator_test(4, MfClassicType1k); | ||||
| } | ||||
| @ -486,6 +490,7 @@ MU_TEST_SUITE(nfc) { | ||||
|     nfc_test_alloc(); | ||||
| 
 | ||||
|     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_4k_4b_file_test); | ||||
|     MU_RUN_TEST(mf_classic_1k_7b_file_test); | ||||
|  | ||||
| @ -1,5 +1,5 @@ | ||||
| App( | ||||
|     appid="sample_apps", | ||||
|     name="Sample apps bundle", | ||||
|     appid="example_apps", | ||||
|     name="Example apps bundle", | ||||
|     apptype=FlipperAppType.METAPACKAGE, | ||||
| ) | ||||
|  | ||||
| @ -50,6 +50,7 @@ static const DuckyKey ducky_keys[] = { | ||||
|     {"ALT-SHIFT", KEY_MOD_LEFT_ALT | KEY_MOD_LEFT_SHIFT}, | ||||
|     {"ALT-GUI", KEY_MOD_LEFT_ALT | KEY_MOD_LEFT_GUI}, | ||||
|     {"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}, | ||||
|     {"CONTROL", KEY_MOD_LEFT_CTRL}, | ||||
| @ -71,8 +72,8 @@ static const DuckyKey ducky_keys[] = { | ||||
|     {"BREAK", HID_KEYBOARD_PAUSE}, | ||||
|     {"PAUSE", HID_KEYBOARD_PAUSE}, | ||||
|     {"CAPSLOCK", HID_KEYBOARD_CAPS_LOCK}, | ||||
|     {"DELETE", HID_KEYBOARD_DELETE}, | ||||
|     {"BACKSPACE", HID_KEYPAD_BACKSPACE}, | ||||
|     {"DELETE", HID_KEYBOARD_DELETE_FORWARD}, | ||||
|     {"BACKSPACE", HID_KEYBOARD_DELETE}, | ||||
|     {"END", HID_KEYBOARD_END}, | ||||
|     {"ESC", 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* baudrate_mode[] = {"Host"}; | ||||
| static const uint32_t baudrate_list[] = { | ||||
|     1200, | ||||
|     2400, | ||||
|     4800, | ||||
|     9600, | ||||
|     19200, | ||||
|     28800, | ||||
|     38400, | ||||
|     57600, | ||||
|     115200, | ||||
|  | ||||
| @ -394,8 +394,8 @@ int32_t snake_game_app(void* p) { | ||||
|         release_mutex(&state_mutex, snake_state); | ||||
|     } | ||||
| 
 | ||||
|     // Wait for all notifications to be played and return backlight to normal state
 | ||||
|     notification_message_block(notification, &sequence_display_backlight_enforce_auto); | ||||
|     // Return backlight to normal state
 | ||||
|     notification_message(notification, &sequence_display_backlight_enforce_auto); | ||||
| 
 | ||||
|     furi_timer_free(timer); | ||||
|     view_port_enabled_set(view_port, false); | ||||
|  | ||||
| @ -3,7 +3,7 @@ | ||||
| #include <furi.h> | ||||
| #include <furi_hal.h> | ||||
| 
 | ||||
| #define WS_VERSION_APP "0.6.1" | ||||
| #define WS_VERSION_APP "0.7" | ||||
| #define WS_DEVELOPED "SkorP" | ||||
| #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_acurite_606tx, | ||||
|     &ws_protocol_acurite_609txc, | ||||
|     &ws_protocol_lacrosse_tx, | ||||
|     &ws_protocol_lacrosse_tx141thbv2, | ||||
|     &ws_protocol_oregon2, | ||||
|     &ws_protocol_acurite_592txr, | ||||
|  | ||||
| @ -8,6 +8,7 @@ | ||||
| #include "gt_wt_03.h" | ||||
| #include "acurite_606tx.h" | ||||
| #include "acurite_609txc.h" | ||||
| #include "lacrosse_tx.h" | ||||
| #include "lacrosse_tx141thbv2.h" | ||||
| #include "oregon2.h" | ||||
| #include "acurite_592txr.h" | ||||
|  | ||||
| @ -5,6 +5,10 @@ | ||||
| #include <stdint.h> | ||||
| #include <stdbool.h> | ||||
| 
 | ||||
| #ifdef __cplusplus | ||||
| extern "C" { | ||||
| #endif | ||||
| 
 | ||||
| typedef struct { | ||||
|     bool enabled; | ||||
| } BtSettings; | ||||
| @ -12,3 +16,7 @@ typedef struct { | ||||
| bool bt_settings_load(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); | ||||
|         } | ||||
|         update_view = true; | ||||
|     } else if(event->key == InputKeyOk) { | ||||
|     } else if(event->key == InputKeyOk && instance->timer) { | ||||
|         if(event->type == InputTypePress) { | ||||
|             furi_timer_start(instance->timer, DESKTOP_SLIDESHOW_POWEROFF_SHORT); | ||||
|         } else if(event->type == InputTypeRelease) { | ||||
|  | ||||
| @ -178,6 +178,47 @@ static void button_menu_process_down(ButtonMenu* button_menu) { | ||||
|         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) { | ||||
|     furi_assert(button_menu); | ||||
| 
 | ||||
| @ -239,6 +280,14 @@ static bool button_menu_view_input_callback(InputEvent* event, void* context) { | ||||
|             consumed = true; | ||||
|             button_menu_process_down(button_menu); | ||||
|             break; | ||||
|         case InputKeyRight: | ||||
|             consumed = true; | ||||
|             button_menu_process_right(button_menu); | ||||
|             break; | ||||
|         case InputKeyLeft: | ||||
|             consumed = true; | ||||
|             button_menu_process_left(button_menu); | ||||
|             break; | ||||
|         default: | ||||
|             break; | ||||
|         } | ||||
|  | ||||
| @ -29,7 +29,7 @@ typedef struct { | ||||
|     TextInputValidatorCallback validator_callback; | ||||
|     void* validator_callback_context; | ||||
|     FuriString* validator_text; | ||||
|     bool valadator_message_visible; | ||||
|     bool validator_message_visible; | ||||
| } TextInputModel; | ||||
| 
 | ||||
| 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) { | ||||
|     if(letter == '_') { | ||||
|         return 0x20; | ||||
|     } else if(isalpha(letter)) { | ||||
|     } else if(islower(letter)) { | ||||
|         return (letter - 0x20); | ||||
|     } else { | ||||
|         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_color(canvas, ColorWhite); | ||||
|         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); | ||||
|     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); | ||||
|     } | ||||
| 
 | ||||
| @ -317,7 +319,7 @@ static void text_input_handle_ok(TextInput* text_input, TextInputModel* model, b | ||||
|         if(model->validator_callback && | ||||
|            (!model->validator_callback( | ||||
|                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); | ||||
|         } else if(model->callback != 0 && text_length > 0) { | ||||
|             model->callback(model->callback_context); | ||||
| @ -329,9 +331,6 @@ static void text_input_handle_ok(TextInput* text_input, TextInputModel* model, b | ||||
|             text_length = 0; | ||||
|         } | ||||
|         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 + 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); | ||||
| 
 | ||||
|     if((!(event->type == InputTypePress) && !(event->type == InputTypeRelease)) && | ||||
|        model->valadator_message_visible) { | ||||
|         model->valadator_message_visible = false; | ||||
|        model->validator_message_visible) { | ||||
|         model->validator_message_visible = false; | ||||
|         consumed = true; | ||||
|     } else if(event->type == InputTypeShort) { | ||||
|         consumed = true; | ||||
| @ -436,7 +435,7 @@ void text_input_timer_callback(void* context) { | ||||
|     with_view_model( | ||||
|         text_input->view, | ||||
|         TextInputModel * model, | ||||
|         { model->valadator_message_visible = false; }, | ||||
|         { model->validator_message_visible = false; }, | ||||
|         true); | ||||
| } | ||||
| 
 | ||||
| @ -496,7 +495,7 @@ void text_input_reset(TextInput* text_input) { | ||||
|             model->validator_callback = NULL; | ||||
|             model->validator_callback_context = NULL; | ||||
|             furi_string_reset(model->validator_text); | ||||
|             model->valadator_message_visible = false; | ||||
|             model->validator_message_visible = false; | ||||
|         }, | ||||
|         true); | ||||
| } | ||||
|  | ||||
| @ -91,7 +91,7 @@ void widget_add_string_element( | ||||
|  * @param[in]  text             Formatted text. The following formats are available: | ||||
|  *                               "\e#Bold text\e#" - bold 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 | ||||
|  */ | ||||
| void widget_add_text_box_element( | ||||
|  | ||||
| @ -19,7 +19,7 @@ extern "C" { | ||||
| typedef enum { | ||||
|     InputTypePress, /**< Press 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  */ | ||||
|     InputTypeRepeat, /**< Repeat event, emitted with INPUT_LONG_PRESS_COUNTS period after InputTypeLong event */ | ||||
|     InputTypeMAX, /**< Special value for exceptional */ | ||||
|  | ||||
| @ -1,5 +1,6 @@ | ||||
| #include <furi.h> | ||||
| #include <furi_hal.h> | ||||
| #include <stm32_adafruit_sd.h> | ||||
| 
 | ||||
| #include <cli/cli.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) { | ||||
|         SDInfo sd_info; | ||||
|         SD_CID sd_cid; | ||||
|         FS_Error error = storage_sd_info(api, &sd_info); | ||||
|         BSP_SD_GetCIDRegister(&sd_cid); | ||||
| 
 | ||||
|         if(error != FSE_OK) { | ||||
|             storage_cli_print_error(error); | ||||
|         } else { | ||||
|             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_api_get_fs_type_text(sd_info.fs_type), | ||||
|                 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 { | ||||
|         storage_cli_print_usage(); | ||||
|  | ||||
| @ -1,4 +1,5 @@ | ||||
| #include "../storage_settings.h" | ||||
| #include <stm32_adafruit_sd.h> | ||||
| 
 | ||||
| static void storage_settings_scene_sd_info_dialog_callback(DialogExResult result, void* context) { | ||||
|     StorageSettings* app = context; | ||||
| @ -11,7 +12,10 @@ void storage_settings_scene_sd_info_on_enter(void* context) { | ||||
|     DialogEx* dialog_ex = app->dialog_ex; | ||||
| 
 | ||||
|     SDInfo sd_info; | ||||
|     SD_CID sd_cid; | ||||
|     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); | ||||
| 
 | ||||
|     dialog_ex_set_context(dialog_ex, app); | ||||
| @ -26,13 +30,22 @@ void storage_settings_scene_sd_info_on_enter(void* context) { | ||||
|     } else { | ||||
|         furi_string_printf( | ||||
|             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_api_get_fs_type_text(sd_info.fs_type), | ||||
|             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, 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); | ||||
|  | ||||
| @ -54,13 +54,14 @@ assetsenv.Alias("proto_ver", proto_ver) | ||||
| 
 | ||||
| # Gather everything into a static lib | ||||
| 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) | ||||
| assetsenv.Install("${LIB_DIST_DIR}", assetslib) | ||||
| 
 | ||||
| 
 | ||||
| # Resources for SD card | ||||
| 
 | ||||
| env.SetDefault(FW_RESOURCES=None) | ||||
| if assetsenv["IS_BASE_FIRMWARE"]: | ||||
|     # External dolphin animations | ||||
|     dolphin_external = assetsenv.DolphinExtBuilder( | ||||
| @ -92,8 +93,7 @@ if assetsenv["IS_BASE_FIRMWARE"]: | ||||
|     ) | ||||
| 
 | ||||
|     # Exporting resources node to external environment | ||||
|     env["FW_ASSETS_HEADERS"] = assets_parts | ||||
|     env["FW_RESOURCES"] = resources | ||||
|     env.Replace(FW_RESOURCES=resources) | ||||
|     assetsenv.Alias("resources", resources) | ||||
| 
 | ||||
| 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 | ||||
| STRING More information about script syntax can be found here: | ||||
| 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 | ||||
| 
 | ||||
| STRING EOF | ||||
|  | ||||
| @ -80,5 +80,5 @@ STRING Flipper Zero BadUSB feature is compatible with USB Rubber Ducky script fo | ||||
| ENTER | ||||
| STRING More information about script syntax can be found here: | ||||
| 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 | ||||
|  | ||||
							
								
								
									
										808
									
								
								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=...`. | ||||
| - `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. | ||||
| - `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. | ||||
| 
 | ||||
| ### Firmware targets | ||||
|  | ||||
| @ -67,6 +67,7 @@ Can be combined with a special key command or a single character. | ||||
| |ALT-SHIFT|ALT+SHIFT| | ||||
| |ALT-GUI|ALT+WIN| | ||||
| |GUI-SHIFT|WIN+SHIFT| | ||||
| |GUI-CTRL|WIN+CTRL| | ||||
| 
 | ||||
| ## String | ||||
| 
 | ||||
|  | ||||
| @ -15,6 +15,7 @@ env = ENV.Clone( | ||||
|         ("compilation_db", {"COMPILATIONDB_COMSTR": "\tCDB\t${TARGET}"}), | ||||
|         "fwbin", | ||||
|         "fbt_apps", | ||||
|         "pvsstudio", | ||||
|     ], | ||||
|     COMPILATIONDB_USE_ABSPATH=False, | ||||
|     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(",")) | ||||
| 
 | ||||
| 
 | ||||
| if fwenv["FAP_EXAMPLES"]: | ||||
|     fwenv.Append(APPDIRS=[("applications/examples", False)]) | ||||
| 
 | ||||
| for app_dir, _ in env["APPDIRS"]: | ||||
|     app_dir_node = env.Dir("#").Dir(app_dir) | ||||
| 
 | ||||
| @ -273,6 +273,24 @@ Precious(fwcdb) | ||||
| NoClean(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 | ||||
| # and link its directory as build/latest | ||||
| if should_gen_cdb_and_link_dir(fwenv, BUILD_TARGETS): | ||||
|  | ||||
| @ -1,5 +1,5 @@ | ||||
| entry,status,name,type,params | ||||
| Version,+,11.6,, | ||||
| Version,+,11.9,, | ||||
| Header,+,applications/services/bt/bt_service/bt.h,, | ||||
| Header,+,applications/services/cli/cli.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/string_stream.h,, | ||||
| Header,+,lib/toolbox/tar/tar_archive.h,, | ||||
| Header,+,lib/toolbox/value_index.h,, | ||||
| Header,+,lib/toolbox/version.h,, | ||||
| Function,-,LL_ADC_CommonDeInit,ErrorStatus,ADC_Common_TypeDef* | ||||
| 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_init,void, | ||||
| 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_restore_lock,int32_t,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_stack_space,uint32_t,FuriThreadId | ||||
| Function,+,furi_thread_get_state,FuriThreadState,FuriThread* | ||||
| Function,+,furi_thread_get_stdout_callback,FuriThreadStdoutWriteCallback, | ||||
| Function,+,furi_thread_is_suspended,_Bool,FuriThreadId | ||||
| Function,+,furi_thread_join,_Bool,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_duty_cycle,float,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_is_protocol_valid,_Bool,InfraredProtocol | ||||
| 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_rewind,_Bool,MfClassicDict* | ||||
| 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_sector_by_block,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,-,pow10,double,double | ||||
| 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_pubsub,FuriPubSub*,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_callback,_Bool,"const char*, FuriString*, void*" | ||||
| 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_current_value_index,uint8_t,VariableItem* | ||||
| 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]; | ||||
| 
 | ||||
|             /* Byte 1 */ | ||||
|             Cid->OEM_AppliID = CID_Tab[1] << 8; | ||||
| 
 | ||||
|             /* Byte 2 */ | ||||
|             Cid->OEM_AppliID |= CID_Tab[2]; | ||||
|             memcpy(Cid->OEM_AppliID, CID_Tab + 1, 2); | ||||
| 
 | ||||
|             /* Byte 3 */ | ||||
|             Cid->ProdName1 = CID_Tab[3] << 24; | ||||
| 
 | ||||
|             /* 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]; | ||||
|             memcpy(Cid->ProdName, CID_Tab + 3, 5); | ||||
| 
 | ||||
|             /* Byte 8 */ | ||||
|             Cid->ProdRev = CID_Tab[8]; | ||||
| @ -815,11 +800,12 @@ uint8_t SD_GetCIDRegister(SD_CID* Cid) { | ||||
|             Cid->ProdSN |= CID_Tab[12]; | ||||
| 
 | ||||
|             /* Byte 13 */ | ||||
|             Cid->Reserved1 |= (CID_Tab[13] & 0xF0) >> 4; | ||||
|             Cid->ManufactDate = (CID_Tab[13] & 0x0F) << 8; | ||||
|             Cid->Reserved1 = (CID_Tab[13] & 0xF0) >> 4; | ||||
|             Cid->ManufactYear = (CID_Tab[13] & 0x0F) << 4; | ||||
| 
 | ||||
|             /* Byte 14 */ | ||||
|             Cid->ManufactDate |= CID_Tab[14]; | ||||
|             Cid->ManufactYear |= (CID_Tab[14] & 0xF0) >> 4; | ||||
|             Cid->ManufactMonth = (CID_Tab[14] & 0x0F); | ||||
| 
 | ||||
|             /* Byte 15 */ | ||||
|             Cid->CID_CRC = (CID_Tab[15] & 0xFE) >> 1; | ||||
| @ -837,6 +823,21 @@ uint8_t SD_GetCIDRegister(SD_CID* Cid) { | ||||
|     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 | ||||
|   * @param  Cmd: The user expected command to send to SD card. | ||||
|  | ||||
| @ -133,16 +133,16 @@ typedef struct { | ||||
|   * @brief  Card Identification Data: CID Register    | ||||
|   */ | ||||
| typedef struct { | ||||
|     __IO uint8_t ManufacturerID; /* ManufacturerID */ | ||||
|     __IO uint16_t OEM_AppliID; /* OEM/Application ID */ | ||||
|     __IO uint32_t ProdName1; /* Product Name part1 */ | ||||
|     __IO uint8_t ProdName2; /* Product Name part2*/ | ||||
|     __IO uint8_t ProdRev; /* Product Revision */ | ||||
|     __IO uint32_t ProdSN; /* Product Serial Number */ | ||||
|     __IO uint8_t Reserved1; /* Reserved1 */ | ||||
|     __IO uint16_t ManufactDate; /* Manufacturing Date */ | ||||
|     __IO uint8_t CID_CRC; /* CID CRC */ | ||||
|     __IO uint8_t Reserved2; /* always 1 */ | ||||
|     uint8_t ManufacturerID; /* ManufacturerID */ | ||||
|     char OEM_AppliID[2]; /* OEM/Application ID */ | ||||
|     char ProdName[5]; /* Product Name */ | ||||
|     uint8_t ProdRev; /* Product Revision */ | ||||
|     uint32_t ProdSN; /* Product Serial Number */ | ||||
|     uint8_t Reserved1; /* Reserved1 */ | ||||
|     uint8_t ManufactYear; /* Manufacturing Year */ | ||||
|     uint8_t ManufactMonth; /* Manufacturing Month */ | ||||
|     uint8_t CID_CRC; /* CID CRC */ | ||||
|     uint8_t Reserved2; /* always 1 */ | ||||
| } SD_CID; | ||||
| 
 | ||||
| /** 
 | ||||
| @ -207,6 +207,7 @@ uint8_t | ||||
| uint8_t BSP_SD_Erase(uint32_t StartAddr, uint32_t EndAddr); | ||||
| uint8_t BSP_SD_GetCardState(void); | ||||
| uint8_t BSP_SD_GetCardInfo(SD_CardInfo* pCardInfo); | ||||
| uint8_t BSP_SD_GetCIDRegister(SD_CID* Cid); | ||||
| 
 | ||||
| /* Link functions for SD Card peripheral*/ | ||||
| void SD_SPI_Slow_Init(void); | ||||
|  | ||||
| @ -52,30 +52,6 @@ extern "C" { | ||||
|     } | ||||
| #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 | ||||
| } | ||||
| #endif | ||||
|  | ||||
| @ -93,7 +93,11 @@ extern "C" { | ||||
| #endif | ||||
| 
 | ||||
| #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 | ||||
| 
 | ||||
| #define FURI_SW_MEMBARRIER() asm volatile("" : : : "memory") | ||||
|  | ||||
| @ -7,8 +7,32 @@ | ||||
| 
 | ||||
| #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() { | ||||
|     furi_assert(!furi_is_irq_context()); | ||||
|     furi_assert(!furi_kernel_is_irq_or_masked()); | ||||
| 
 | ||||
|     int32_t lock; | ||||
| 
 | ||||
| @ -33,7 +57,7 @@ int32_t furi_kernel_lock() { | ||||
| } | ||||
| 
 | ||||
| int32_t furi_kernel_unlock() { | ||||
|     furi_assert(!furi_is_irq_context()); | ||||
|     furi_assert(!furi_kernel_is_irq_or_masked()); | ||||
| 
 | ||||
|     int32_t lock; | ||||
| 
 | ||||
| @ -63,7 +87,7 @@ int32_t furi_kernel_unlock() { | ||||
| } | ||||
| 
 | ||||
| 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()) { | ||||
|     case taskSCHEDULER_SUSPENDED: | ||||
| @ -99,7 +123,7 @@ uint32_t furi_kernel_get_tick_frequency() { | ||||
| } | ||||
| 
 | ||||
| void furi_delay_tick(uint32_t ticks) { | ||||
|     furi_assert(!furi_is_irq_context()); | ||||
|     furi_assert(!furi_kernel_is_irq_or_masked()); | ||||
|     if(ticks == 0U) { | ||||
|         taskYIELD(); | ||||
|     } else { | ||||
| @ -108,7 +132,7 @@ void furi_delay_tick(uint32_t ticks) { | ||||
| } | ||||
| 
 | ||||
| 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; | ||||
|     FuriStatus stat; | ||||
| @ -137,7 +161,7 @@ FuriStatus furi_delay_until_tick(uint32_t tick) { | ||||
| uint32_t furi_get_tick() { | ||||
|     TickType_t ticks; | ||||
| 
 | ||||
|     if(furi_is_irq_context() != 0U) { | ||||
|     if(furi_kernel_is_irq_or_masked() != 0U) { | ||||
|         ticks = xTaskGetTickCountFromISR(); | ||||
|     } else { | ||||
|         ticks = xTaskGetTickCount(); | ||||
|  | ||||
| @ -10,19 +10,42 @@ | ||||
| extern "C" { | ||||
| #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
 | ||||
|  * | ||||
|  * @warning This should never be called in interrupt request context. | ||||
|  * | ||||
|  * @return     previous lock state(0 - unlocked, 1 - locked) | ||||
|  */ | ||||
| int32_t furi_kernel_lock(); | ||||
| 
 | ||||
| /** Unlock kernel, resume process scheduling
 | ||||
|  * | ||||
|  * @warning This should never be called in interrupt request context. | ||||
|  * | ||||
|  * @return     previous lock state(0 - unlocked, 1 - locked) | ||||
|  */ | ||||
| int32_t furi_kernel_unlock(); | ||||
| 
 | ||||
| /** Restore kernel lock state
 | ||||
|  * | ||||
|  * @warning This should never be called in interrupt request context. | ||||
|  * | ||||
|  * @param[in]  lock  The lock state | ||||
|  * | ||||
| @ -37,6 +60,8 @@ int32_t furi_kernel_restore_lock(int32_t lock); | ||||
| uint32_t furi_kernel_get_tick_frequency(); | ||||
| 
 | ||||
| /** Delay execution
 | ||||
|  * | ||||
|  * @warning This should never be called in interrupt request context. | ||||
|  * | ||||
|  * Also keep in mind delay is aliased to scheduler timer intervals. | ||||
|  * | ||||
| @ -45,6 +70,8 @@ uint32_t furi_kernel_get_tick_frequency(); | ||||
| void furi_delay_tick(uint32_t ticks); | ||||
| 
 | ||||
| /** 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 | ||||
|  * | ||||
|  | ||||
| @ -1,11 +1,11 @@ | ||||
| #include "kernel.h" | ||||
| #include "message_queue.h" | ||||
| #include "core/common_defines.h" | ||||
| #include <FreeRTOS.h> | ||||
| #include <queue.h> | ||||
| #include "check.h" | ||||
| 
 | ||||
| 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); | ||||
|     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) { | ||||
|     furi_assert(furi_is_irq_context() == 0U); | ||||
|     furi_assert(furi_kernel_is_irq_or_masked() == 0U); | ||||
|     furi_assert(instance); | ||||
| 
 | ||||
|     vQueueDelete((QueueHandle_t)instance); | ||||
| @ -28,7 +28,7 @@ FuriStatus | ||||
| 
 | ||||
|     stat = FuriStatusOk; | ||||
| 
 | ||||
|     if(furi_is_irq_context() != 0U) { | ||||
|     if(furi_kernel_is_irq_or_masked() != 0U) { | ||||
|         if((hQueue == NULL) || (msg_ptr == NULL) || (timeout != 0U)) { | ||||
|             stat = FuriStatusErrorParameter; | ||||
|         } else { | ||||
| @ -65,7 +65,7 @@ FuriStatus furi_message_queue_get(FuriMessageQueue* instance, void* msg_ptr, uin | ||||
| 
 | ||||
|     stat = FuriStatusOk; | ||||
| 
 | ||||
|     if(furi_is_irq_context() != 0U) { | ||||
|     if(furi_kernel_is_irq_or_masked() != 0U) { | ||||
|         if((hQueue == NULL) || (msg_ptr == NULL) || (timeout != 0U)) { | ||||
|             stat = FuriStatusErrorParameter; | ||||
|         } else { | ||||
| @ -131,7 +131,7 @@ uint32_t furi_message_queue_get_count(FuriMessageQueue* instance) { | ||||
| 
 | ||||
|     if(hQueue == NULL) { | ||||
|         count = 0U; | ||||
|     } else if(furi_is_irq_context() != 0U) { | ||||
|     } else if(furi_kernel_is_irq_or_masked() != 0U) { | ||||
|         count = uxQueueMessagesWaitingFromISR(hQueue); | ||||
|     } else { | ||||
|         count = uxQueueMessagesWaiting(hQueue); | ||||
| @ -148,7 +148,7 @@ uint32_t furi_message_queue_get_space(FuriMessageQueue* instance) { | ||||
| 
 | ||||
|     if(mq == NULL) { | ||||
|         space = 0U; | ||||
|     } else if(furi_is_irq_context() != 0U) { | ||||
|     } else if(furi_kernel_is_irq_or_masked() != 0U) { | ||||
|         isrm = taskENTER_CRITICAL_FROM_ISR(); | ||||
| 
 | ||||
|         /* space = pxQueue->uxLength - pxQueue->uxMessagesWaiting; */ | ||||
| @ -167,7 +167,7 @@ FuriStatus furi_message_queue_reset(FuriMessageQueue* instance) { | ||||
|     QueueHandle_t hQueue = (QueueHandle_t)instance; | ||||
|     FuriStatus stat; | ||||
| 
 | ||||
|     if(furi_is_irq_context() != 0U) { | ||||
|     if(furi_kernel_is_irq_or_masked() != 0U) { | ||||
|         stat = FuriStatusErrorISR; | ||||
|     } else if(hQueue == NULL) { | ||||
|         stat = FuriStatusErrorParameter; | ||||
|  | ||||
| @ -530,6 +530,12 @@ bool furi_thread_set_stdout_callback(FuriThreadStdoutWriteCallback callback) { | ||||
|     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) { | ||||
|     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); | ||||
| 
 | ||||
| /** Get STDOUT callback for thead
 | ||||
|  * | ||||
|  * @return STDOUT callback | ||||
|  */ | ||||
| FuriThreadStdoutWriteCallback furi_thread_get_stdout_callback(); | ||||
| 
 | ||||
| /** Set STDOUT callback for thread
 | ||||
|  *  | ||||
|  * @param      callback  callback or NULL to clear | ||||
|  | ||||
| @ -3,7 +3,6 @@ | ||||
| #include "memmgr.h" | ||||
| #include "kernel.h" | ||||
| 
 | ||||
| #include "core/common_defines.h" | ||||
| #include <FreeRTOS.h> | ||||
| #include <timers.h> | ||||
| 
 | ||||
| @ -27,7 +26,7 @@ static void TimerCallback(TimerHandle_t hTimer) { | ||||
| } | ||||
| 
 | ||||
| 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; | ||||
|     TimerCallback_t* callb; | ||||
| @ -60,7 +59,7 @@ FuriTimer* furi_timer_alloc(FuriTimerCallback func, FuriTimerType type, void* co | ||||
| } | ||||
| 
 | ||||
| void furi_timer_free(FuriTimer* instance) { | ||||
|     furi_assert(!furi_is_irq_context()); | ||||
|     furi_assert(!furi_kernel_is_irq_or_masked()); | ||||
|     furi_assert(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) { | ||||
|     furi_assert(!furi_is_irq_context()); | ||||
|     furi_assert(!furi_kernel_is_irq_or_masked()); | ||||
|     furi_assert(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) { | ||||
|     furi_assert(!furi_is_irq_context()); | ||||
|     furi_assert(!furi_kernel_is_irq_or_masked()); | ||||
|     furi_assert(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) { | ||||
|     furi_assert(!furi_is_irq_context()); | ||||
|     furi_assert(!furi_kernel_is_irq_or_masked()); | ||||
|     furi_assert(instance); | ||||
| 
 | ||||
|     TimerHandle_t hTimer = (TimerHandle_t)instance; | ||||
|  | ||||
| @ -3,7 +3,7 @@ | ||||
| #include "queue.h" | ||||
| 
 | ||||
| void furi_init() { | ||||
|     furi_assert(!furi_is_irq_context()); | ||||
|     furi_assert(!furi_kernel_is_irq_or_masked()); | ||||
|     furi_assert(xTaskGetSchedulerState() == taskSCHEDULER_NOT_STARTED); | ||||
| 
 | ||||
|     furi_log_init(); | ||||
| @ -11,7 +11,7 @@ void furi_init() { | ||||
| } | ||||
| 
 | ||||
| void furi_run() { | ||||
|     furi_assert(!furi_is_irq_context()); | ||||
|     furi_assert(!furi_kernel_is_irq_or_masked()); | ||||
|     furi_assert(xTaskGetSchedulerState() == taskSCHEDULER_NOT_STARTED); | ||||
| 
 | ||||
| #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)); | ||||
|         break; | ||||
|     case R_ARM_THM_PC22: | ||||
|     case R_ARM_CALL: | ||||
|     case R_ARM_THM_JUMP24: | ||||
|         elf_relocate_jmp_call(elf, relAddr, type, symAddr); | ||||
|         FURI_LOG_D( | ||||
|  | ||||
| @ -1,5 +1,6 @@ | ||||
| #include "flipper_application.h" | ||||
| #include "elf/elf_file.h" | ||||
| #include <notification/notification_messages.h> | ||||
| 
 | ||||
| #define TAG "fapp" | ||||
| 
 | ||||
| @ -95,6 +96,15 @@ static int32_t flipper_application_thread(void* context) { | ||||
|     elf_file_pre_run(last_loaded_app->elf); | ||||
|     int32_t result = elf_file_run(last_loaded_app->elf, context); | ||||
|     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; | ||||
| } | ||||
| 
 | ||||
|  | ||||
| @ -1,11 +1,8 @@ | ||||
| #include "infrared_common_i.h" | ||||
| 
 | ||||
| #include <stdlib.h> | ||||
| #include <core/check.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); | ||||
| 
 | ||||
|  | ||||
| @ -1,10 +1,9 @@ | ||||
| #include <core/check.h> | ||||
| #include "infrared.h" | ||||
| #include "infrared_common_i.h" | ||||
| #include <stdbool.h> | ||||
| #include <furi.h> | ||||
| #include "infrared_i.h" | ||||
| #include <stdint.h> | ||||
| 
 | ||||
| #include <stdlib.h> | ||||
| #include <string.h> | ||||
| #include <core/check.h> | ||||
| #include <core/common_defines.h> | ||||
| 
 | ||||
| static InfraredStatus | ||||
|     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 <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 <furi.h> | ||||
| #include "infrared_i.h" | ||||
| #include <furi_hal_infrared.h> | ||||
| #include <string.h> | ||||
| #include <core/check.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 { | ||||
|     InfraredAlloc alloc; | ||||
| @ -36,7 +39,7 @@ struct InfraredEncoderHandler { | ||||
| typedef struct { | ||||
|     InfraredEncoders encoder; | ||||
|     InfraredDecoders decoder; | ||||
|     InfraredGetProtocolSpec get_protocol_spec; | ||||
|     InfraredGetProtocolVariant get_protocol_variant; | ||||
| } InfraredEncoderDecoder; | ||||
| 
 | ||||
| static const InfraredEncoderDecoder infrared_encoder_decoder[] = { | ||||
| @ -52,7 +55,7 @@ static const InfraredEncoderDecoder infrared_encoder_decoder[] = { | ||||
|              .encode = infrared_encoder_nec_encode, | ||||
|              .reset = infrared_encoder_nec_reset, | ||||
|              .free = infrared_encoder_nec_free}, | ||||
|         .get_protocol_spec = infrared_nec_get_spec, | ||||
|         .get_protocol_variant = infrared_protocol_nec_get_variant, | ||||
|     }, | ||||
|     { | ||||
|         .decoder = | ||||
| @ -66,7 +69,7 @@ static const InfraredEncoderDecoder infrared_encoder_decoder[] = { | ||||
|              .encode = infrared_encoder_samsung32_encode, | ||||
|              .reset = infrared_encoder_samsung32_reset, | ||||
|              .free = infrared_encoder_samsung32_free}, | ||||
|         .get_protocol_spec = infrared_samsung32_get_spec, | ||||
|         .get_protocol_variant = infrared_protocol_samsung32_get_variant, | ||||
|     }, | ||||
|     { | ||||
|         .decoder = | ||||
| @ -80,7 +83,7 @@ static const InfraredEncoderDecoder infrared_encoder_decoder[] = { | ||||
|              .encode = infrared_encoder_rc5_encode, | ||||
|              .reset = infrared_encoder_rc5_reset, | ||||
|              .free = infrared_encoder_rc5_free}, | ||||
|         .get_protocol_spec = infrared_rc5_get_spec, | ||||
|         .get_protocol_variant = infrared_protocol_rc5_get_variant, | ||||
|     }, | ||||
|     { | ||||
|         .decoder = | ||||
| @ -94,7 +97,7 @@ static const InfraredEncoderDecoder infrared_encoder_decoder[] = { | ||||
|              .encode = infrared_encoder_rc6_encode, | ||||
|              .reset = infrared_encoder_rc6_reset, | ||||
|              .free = infrared_encoder_rc6_free}, | ||||
|         .get_protocol_spec = infrared_rc6_get_spec, | ||||
|         .get_protocol_variant = infrared_protocol_rc6_get_variant, | ||||
|     }, | ||||
|     { | ||||
|         .decoder = | ||||
| @ -108,7 +111,7 @@ static const InfraredEncoderDecoder infrared_encoder_decoder[] = { | ||||
|              .encode = infrared_encoder_sirc_encode, | ||||
|              .reset = infrared_encoder_sirc_reset, | ||||
|              .free = infrared_encoder_sirc_free}, | ||||
|         .get_protocol_spec = infrared_sirc_get_spec, | ||||
|         .get_protocol_variant = infrared_protocol_sirc_get_variant, | ||||
|     }, | ||||
|     { | ||||
|         .decoder = | ||||
| @ -122,13 +125,12 @@ static const InfraredEncoderDecoder infrared_encoder_decoder[] = { | ||||
|              .encode = infrared_encoder_kaseikyo_encode, | ||||
|              .reset = infrared_encoder_kaseikyo_reset, | ||||
|              .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 const InfraredProtocolSpecification* | ||||
|     infrared_get_spec_by_protocol(InfraredProtocol protocol); | ||||
| static const InfraredProtocolVariant* infrared_get_variant_by_protocol(InfraredProtocol protocol); | ||||
| 
 | ||||
| const InfraredMessage* | ||||
|     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) { | ||||
|     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; | ||||
|         } | ||||
|     } | ||||
| @ -282,34 +284,37 @@ InfraredProtocol infrared_get_protocol_by_name(const char* protocol_name) { | ||||
|     return InfraredProtocolUnknown; | ||||
| } | ||||
| 
 | ||||
| static const InfraredProtocolSpecification* | ||||
|     infrared_get_spec_by_protocol(InfraredProtocol protocol) { | ||||
| static const InfraredProtocolVariant* infrared_get_variant_by_protocol(InfraredProtocol protocol) { | ||||
|     int index = infrared_find_index_by_protocol(protocol); | ||||
|     const InfraredProtocolSpecification* spec = NULL; | ||||
|     const InfraredProtocolVariant* variant = NULL; | ||||
|     if(index >= 0) { | ||||
|         spec = infrared_encoder_decoder[index].get_protocol_spec(protocol); | ||||
|         variant = infrared_encoder_decoder[index].get_protocol_variant(protocol); | ||||
|     } | ||||
| 
 | ||||
|     furi_assert(spec); | ||||
|     return spec; | ||||
|     furi_assert(variant); | ||||
|     return variant; | ||||
| } | ||||
| 
 | ||||
| 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) { | ||||
|     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) { | ||||
|     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) { | ||||
|     return infrared_get_spec_by_protocol(protocol)->frequency; | ||||
|     return infrared_get_variant_by_protocol(protocol)->frequency; | ||||
| } | ||||
| 
 | ||||
| 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 | ||||
| 
 | ||||
| #include <stdbool.h> | ||||
| #include <stddef.h> | ||||
| #include <stdint.h> | ||||
| 
 | ||||
| #ifdef __cplusplus | ||||
| @ -201,6 +202,15 @@ uint32_t infrared_get_protocol_frequency(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 | ||||
| } | ||||
| #endif | ||||
|  | ||||
| @ -22,9 +22,10 @@ typedef struct { | ||||
|     uint8_t command_length; | ||||
|     uint32_t frequency; | ||||
|     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 (*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_defs_i.h" | ||||
| #include <stdbool.h> | ||||
| #include <stdint.h> | ||||
| #include <furi.h> | ||||
| #include "../infrared_i.h" | ||||
| #include "infrared_protocol_kaseikyo_i.h" | ||||
| #include <core/check.h> | ||||
| 
 | ||||
| InfraredMessage* infrared_decoder_kaseikyo_check_ready(void* 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) { | ||||
|     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) { | ||||
|  | ||||
| @ -1,9 +1,5 @@ | ||||
| #include "infrared_protocol_kaseikyo_i.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) { | ||||
|     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) { | ||||
|     return infrared_common_encoder_alloc(&protocol_kaseikyo); | ||||
|     return infrared_common_encoder_alloc(&infrared_protocol_kaseikyo); | ||||
| } | ||||
| 
 | ||||
| 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.h" | ||||
| #include "infrared_protocol_defs_i.h" | ||||
| #include <stdbool.h> | ||||
| #include <stdint.h> | ||||
| #include <furi.h> | ||||
| #include "../infrared_i.h" | ||||
| #include "infrared_protocol_nec_i.h" | ||||
| #include <core/check.h> | ||||
| 
 | ||||
| InfraredMessage* infrared_decoder_nec_check_ready(void* 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) { | ||||
|     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) { | ||||
|  | ||||
| @ -1,10 +1,7 @@ | ||||
| #include "infrared_protocol_nec_i.h" | ||||
| 
 | ||||
| #include <core/core_defines.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[] = { | ||||
|     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) { | ||||
|     return infrared_common_encoder_alloc(&protocol_nec); | ||||
|     return infrared_common_encoder_alloc(&infrared_protocol_nec); | ||||
| } | ||||
| 
 | ||||
| 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 <stdbool.h> | ||||
| #include <stddef.h> | ||||
| #include <stdint.h> | ||||
| #include <furi.h> | ||||
| #include "../infrared_i.h" | ||||
| #include "../infrared_protocol_defs_i.h" | ||||
| #include "infrared_protocol_rc5_i.h" | ||||
| 
 | ||||
| #include <stdlib.h> | ||||
| #include <core/check.h> | ||||
| 
 | ||||
| typedef struct { | ||||
|     InfraredCommonDecoder* common_decoder; | ||||
| @ -60,7 +57,7 @@ bool infrared_decoder_rc5_interpret(InfraredCommonDecoder* decoder) { | ||||
| void* infrared_decoder_rc5_alloc(void) { | ||||
|     InfraredRc5Decoder* decoder = malloc(sizeof(InfraredRc5Decoder)); | ||||
|     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; | ||||
|     return decoder; | ||||
| } | ||||
|  | ||||
| @ -1,9 +1,7 @@ | ||||
| #include <core/memmgr.h> | ||||
| #include "infrared.h" | ||||
| #include "common/infrared_common_i.h" | ||||
| #include "infrared_protocol_defs_i.h" | ||||
| #include <stdint.h> | ||||
| #include "../infrared_i.h" | ||||
| #include "infrared_protocol_rc5_i.h" | ||||
| 
 | ||||
| #include <stdlib.h> | ||||
| #include <core/check.h> | ||||
| 
 | ||||
| typedef struct InfraredEncoderRC5 { | ||||
|     InfraredCommonEncoder* common_encoder; | ||||
| @ -41,7 +39,7 @@ InfraredStatus infrared_encoder_rc5_encode(void* encoder_ptr, uint32_t* duration | ||||
| 
 | ||||
| void* infrared_encoder_rc5_alloc(void) { | ||||
|     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; | ||||
|     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