Merge branch 'dev' into patch-custom-font
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 +1 @@
|
|||
--rules-config .pvsconfig -e lib/fatfs -e lib/fnv1a-hash -e lib/FreeRTOS-Kernel -e lib/heatshrink -e lib/libusb_stm32 -e lib/littlefs -e lib/mbedtls -e lib/micro-ecc -e lib/microtar -e lib/mlib -e lib/qrcode -e lib/ST25RFAL002 -e lib/STM32CubeWB -e lib/u8g2 -e lib/nanopb -e */arm-none-eabi/* -e applications/plugins/dap_link/lib/free-dap
|
||||
--ignore-ccache -C gccarm --rules-config .pvsconfig -e lib/fatfs -e lib/fnv1a-hash -e lib/FreeRTOS-Kernel -e lib/heatshrink -e lib/libusb_stm32 -e lib/littlefs -e lib/mbedtls -e lib/micro-ecc -e lib/microtar -e lib/mlib -e lib/qrcode -e lib/ST25RFAL002 -e lib/STM32CubeWB -e lib/u8g2 -e lib/nanopb -e */arm-none-eabi/* -e applications/plugins/dap_link/lib/free-dap
|
||||
|
|
22
.vscode/example/tasks.json
vendored
|
@ -105,6 +105,12 @@
|
|||
"type": "shell",
|
||||
"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",
|
||||
|
@ -145,7 +163,7 @@
|
|||
"command": "./fbt cli",
|
||||
"group": "none",
|
||||
"isBackground": true,
|
||||
"options": {
|
||||
"options": {
|
||||
"env": {
|
||||
"FBT_NO_SYNC": "0"
|
||||
}
|
||||
|
@ -162,4 +180,4 @@
|
|||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
|
@ -52,7 +52,7 @@ Almost everything in flipper firmware is built around this concept.
|
|||
|
||||
## Naming
|
||||
|
||||
### Type names are CamelCase
|
||||
### Type names are PascalCase
|
||||
|
||||
Examples:
|
||||
|
||||
|
|
17
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",
|
||||
*(
|
||||
distenv.Install(
|
||||
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 */
|
||||
|
|
|
@ -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 KiB |
Before Width: | Height: | Size: 1.9 KiB After Width: | Height: | Size: 2 KiB |
Before Width: | Height: | Size: 1.9 KiB After Width: | Height: | Size: 2 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
|
||||
|
|
814
assets/resources/infrared/assets/tv.ir
Executable file → Normal file
|
@ -56,6 +56,7 @@ To run cleanup (think of `make clean`) for specified targets, add the `-c` optio
|
|||
- `get_stlink` - output serial numbers for attached STLink probes. Used for specifying an adapter with `OPENOCD_ADAPTER_SERIAL=...`.
|
||||
- `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
|
||||
|
|
|
@ -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*"
|
||||
|
@ -1390,6 +1391,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,
|
||||
|
@ -1514,6 +1516,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*
|
||||
|
@ -1631,6 +1634,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*
|
||||
|
@ -1880,7 +1884,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
|
||||
|
@ -2066,7 +2070,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*
|
||||
|
@ -2804,6 +2808,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*"
|
||||
|
|
|
|
@ -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,7 +60,9 @@ 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.
|
||||
*
|
||||
* @param[in] ticks The ticks count to pause
|
||||
|
@ -45,6 +70,8 @@ uint32_t furi_kernel_get_tick_frequency();
|
|||
void furi_delay_tick(uint32_t ticks);
|
||||
|
||||
/** 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;
|
||||
}
|
38
lib/infrared/encoder_decoder/rc5/infrared_protocol_rc5.h
Normal file
|
@ -0,0 +1,38 @@
|
|||
#pragma once
|
||||
|
||||
#include "../infrared_i.h"
|
||||
|
||||
/***************************************************************************************************
|
||||
* 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
|
||||
***************************************************************************************************/
|
||||
|
||||
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);
|
||||
|
||||
const InfraredProtocolVariant* infrared_protocol_rc5_get_variant(InfraredProtocol protocol);
|
20
lib/infrared/encoder_decoder/rc5/infrared_protocol_rc5_i.h
Normal file
|
@ -0,0 +1,20 @@
|
|||
#pragma once
|
||||
|
||||
#include "../common/infrared_common_i.h"
|
||||
|
||||
#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
|
||||
#define INFRARED_RC5_REPEAT_COUNT_MIN 1
|
||||
|
||||
extern const InfraredCommonProtocolSpec infrared_protocol_rc5;
|
||||
|
||||
bool infrared_decoder_rc5_interpret(InfraredCommonDecoder* decoder);
|
|
@ -1,27 +0,0 @@
|
|||
#include "../infrared_i.h"
|
||||
#include "infrared_protocol_defs_i.h"
|
||||
|
||||
static const InfraredProtocolSpecification infrared_rc5_protocol_specification = {
|
||||
.name = "RC5",
|
||||
.address_length = 5,
|
||||
.command_length = 6,
|
||||
.frequency = INFRARED_RC5_CARRIER_FREQUENCY,
|
||||
.duty_cycle = INFRARED_RC5_DUTY_CYCLE,
|
||||
};
|
||||
|
||||
static const InfraredProtocolSpecification infrared_rc5x_protocol_specification = {
|
||||
.name = "RC5X",
|
||||
.address_length = 5,
|
||||
.command_length = 7,
|
||||
.frequency = INFRARED_RC5_CARRIER_FREQUENCY,
|
||||
.duty_cycle = INFRARED_RC5_DUTY_CYCLE,
|
||||
};
|
||||
|
||||
const InfraredProtocolSpecification* infrared_rc5_get_spec(InfraredProtocol protocol) {
|
||||
if(protocol == InfraredProtocolRC5)
|
||||
return &infrared_rc5_protocol_specification;
|
||||
else if(protocol == InfraredProtocolRC5X)
|
||||
return &infrared_rc5x_protocol_specification;
|
||||
else
|
||||
return NULL;
|
||||
}
|
|
@ -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_rc6_i.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <core/check.h>
|
||||
|
||||
typedef struct {
|
||||
InfraredCommonDecoder* common_decoder;
|
||||
|
@ -93,7 +90,7 @@ InfraredStatus infrared_decoder_rc6_decode_manchester(
|
|||
void* infrared_decoder_rc6_alloc(void) {
|
||||
InfraredRc6Decoder* decoder = malloc(sizeof(InfraredRc6Decoder));
|
||||
decoder->toggle = false;
|
||||
decoder->common_decoder = infrared_common_decoder_alloc(&protocol_rc6);
|
||||
decoder->common_decoder = infrared_common_decoder_alloc(&infrared_protocol_rc6);
|
||||
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_rc6_i.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <core/check.h>
|
||||
|
||||
typedef struct InfraredEncoderRC6 {
|
||||
InfraredCommonEncoder* common_encoder;
|
||||
|
@ -35,7 +33,7 @@ InfraredStatus infrared_encoder_rc6_encode(void* encoder_ptr, uint32_t* duration
|
|||
|
||||
void* infrared_encoder_rc6_alloc(void) {
|
||||
InfraredEncoderRC6* encoder = malloc(sizeof(InfraredEncoderRC6));
|
||||
encoder->common_encoder = infrared_common_encoder_alloc(&protocol_rc6);
|
||||
encoder->common_encoder = infrared_common_encoder_alloc(&infrared_protocol_rc6);
|
||||
encoder->toggle_bit = false;
|
||||
return encoder;
|
||||
}
|
||||
|
|
39
lib/infrared/encoder_decoder/rc6/infrared_protocol_rc6.c
Normal file
|
@ -0,0 +1,39 @@
|
|||
#include "infrared_protocol_rc6_i.h"
|
||||
|
||||
const InfraredCommonProtocolSpec infrared_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,
|
||||
};
|
||||
|
||||
static const InfraredProtocolVariant infrared_protocol_variant_rc6 = {
|
||||
.name = "RC6",
|
||||
.address_length = 8,
|
||||
.command_length = 8,
|
||||
.frequency = INFRARED_RC6_CARRIER_FREQUENCY,
|
||||
.duty_cycle = INFRARED_RC6_DUTY_CYCLE,
|
||||
.repeat_count = INFRARED_RC6_REPEAT_COUNT_MIN,
|
||||
};
|
||||
|
||||
const InfraredProtocolVariant* infrared_protocol_rc6_get_variant(InfraredProtocol protocol) {
|
||||
if(protocol == InfraredProtocolRC6)
|
||||
return &infrared_protocol_variant_rc6;
|
||||
else
|
||||
return NULL;
|
||||
}
|
37
lib/infrared/encoder_decoder/rc6/infrared_protocol_rc6.h
Normal file
|
@ -0,0 +1,37 @@
|
|||
#pragma once
|
||||
|
||||
#include "../infrared_i.h"
|
||||
|
||||
/***************************************************************************************************
|
||||
* 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
|
||||
***************************************************************************************************/
|
||||
|
||||
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);
|
||||
|
||||
const InfraredProtocolVariant* infrared_protocol_rc6_get_variant(InfraredProtocol protocol);
|