Merge branch 'mouse_jiggler_interval_selction_added' of https://github.com/DocKuro/unleashed-firmware into mouse_jiggler_interval_selction_added

This commit is contained in:
Emanuele Conti 2023-02-02 10:33:54 +00:00
commit cee11464de
72 changed files with 1121 additions and 145 deletions

View file

@ -52,7 +52,7 @@ Almost everything in flipper firmware is built around this concept.
## Naming
### Type names are CamelCase
### Type names are PascalCase
Examples:

View file

@ -11,4 +11,5 @@ App(
stack_size=1 * 1024,
order=130,
fap_category="Debug",
fap_libs=["assets"],
)

View 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);
}

View 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);

View file

@ -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);

View file

@ -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;

View file

@ -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",

View file

@ -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) {

View file

@ -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;
}

View file

@ -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);

View file

@ -54,6 +54,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},

View file

@ -278,6 +278,7 @@ void dealer_tick(GameState* game_state) {
if(dealer_score >= DEALER_MAX) {
if(dealer_score > 21 || dealer_score < player_score) {
DOLPHIN_DEED(DolphinDeedPluginGameWin);
enqueue(
&(game_state->queue_state),
game_state,
@ -570,6 +571,7 @@ int32_t blackjack_app(void* p) {
gui_add_view_port(gui, view_port, GuiLayerFullscreen);
AppEvent event;
DOLPHIN_DEED(DolphinDeedPluginGameStart);
for(bool processing = true; processing;) {
FuriStatus event_status = furi_message_queue_get(event_queue, &event, 100);

View file

@ -47,7 +47,8 @@ static void hid_mouse_jiggler_draw_callback(Canvas* canvas, void* context) {
if(model->interval_idx != LENGTH(intervals) - 1)
canvas_draw_icon(canvas, 80, 19, &I_ButtonRight_4x7);
FuriString* interval_str = furi_string_alloc_printf("%d", intervals[model->interval_idx]);
elements_multiline_text(canvas, 91, 26, furi_string_get_cstr(interval_str));
elements_multiline_text(canvas, 91, 26, furi_furi_string_get_cstr(interval_str));
furi_string_free(interval_str);
canvas_set_font(canvas, FontPrimary);
elements_multiline_text(canvas, AlignLeft, 40, "Press Start\nto jiggle");

View file

@ -392,8 +392,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);

View file

@ -277,6 +277,7 @@ void tick(GameState* game_state, NotificationApp* notification) {
if(game_state->state == GameStatePlay) {
if(game_state->top_cards[0].character == 11 && game_state->top_cards[1].character == 11 &&
game_state->top_cards[2].character == 11 && game_state->top_cards[3].character == 11) {
DOLPHIN_DEED(DolphinDeedPluginGameWin);
game_state->state = GameStateAnimate;
return;
}
@ -490,6 +491,8 @@ int32_t solitaire_app(void* p) {
gui_add_view_port(gui, view_port, GuiLayerFullscreen);
AppEvent event;
DOLPHIN_DEED(DolphinDeedPluginGameStart);
for(bool processing = true; processing;) {
FuriStatus event_status = furi_message_queue_get(event_queue, &event, 150);
GameState* localstate = (GameState*)acquire_mutex_block(&state_mutex);

View file

@ -38,6 +38,7 @@ static void hid_mouse_jiggler_draw_callback(Canvas* canvas, void* context) {
canvas_draw_icon(canvas, 80, 19, &I_ButtonRight_4x7);
FuriString* interval_str = furi_string_alloc_printf("%d", intervals[model->interval_idx]);
elements_multiline_text(canvas, 91, 26, furi_string_get_cstr(interval_str));
furi_string_free(interval_str);
canvas_set_font(canvas, FontPrimary);
elements_multiline_text(canvas, AlignLeft, 40, "Press Start\nto jiggle");

View file

@ -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"

View 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);
}

View 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);

View file

@ -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,

View file

@ -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"

View file

@ -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

View file

@ -1,5 +1,6 @@
#include <furi.h>
#include <furi_hal.h>
#include <stm32_adafruit_sd.h>
#include <cli/cli.h>
#include <lib/toolbox/args.h>
@ -60,17 +61,28 @@ static void storage_cli_info(Cli* cli, FuriString* path) {
}
} else if(furi_string_cmp_str(path, STORAGE_EXT_PATH_PREFIX) == 0) {
SDInfo sd_info;
SD_CID sd_cid;
FS_Error error = storage_sd_info(api, &sd_info);
BSP_SD_GetCIDRegister(&sd_cid);
if(error != FSE_OK) {
storage_cli_print_error(error);
} else {
printf(
"Label: %s\r\nType: %s\r\n%luKiB total\r\n%luKiB free\r\n",
"Label: %s\r\nType: %s\r\n%luKiB total\r\n%luKiB free\r\n"
"%02x%2.2s %5.5s %i.%i\r\nSN:%04lx %02i/%i\r\n",
sd_info.label,
sd_api_get_fs_type_text(sd_info.fs_type),
sd_info.kb_total,
sd_info.kb_free);
sd_info.kb_free,
sd_cid.ManufacturerID,
sd_cid.OEM_AppliID,
sd_cid.ProdName,
sd_cid.ProdRev >> 4,
sd_cid.ProdRev & 0xf,
sd_cid.ProdSN,
sd_cid.ManufactMonth,
sd_cid.ManufactYear + 2000);
}
} else {
storage_cli_print_usage();

View file

@ -1,4 +1,5 @@
#include "../storage_settings.h"
#include <stm32_adafruit_sd.h>
static void storage_settings_scene_sd_info_dialog_callback(DialogExResult result, void* context) {
StorageSettings* app = context;
@ -11,7 +12,10 @@ void storage_settings_scene_sd_info_on_enter(void* context) {
DialogEx* dialog_ex = app->dialog_ex;
SDInfo sd_info;
SD_CID sd_cid;
FS_Error sd_status = storage_sd_info(app->fs_api, &sd_info);
BSP_SD_GetCIDRegister(&sd_cid);
scene_manager_set_scene_state(app->scene_manager, StorageSettingsSDInfo, sd_status);
dialog_ex_set_context(dialog_ex, app);
@ -26,13 +30,22 @@ void storage_settings_scene_sd_info_on_enter(void* context) {
} else {
furi_string_printf(
app->text_string,
"Label: %s\nType: %s\n%lu KiB total\n%lu KiB free",
"Label: %s\nType: %s\n%lu KiB total\n%lu KiB free\n"
"%02X%2.2s %5.5s %i.%i\nSN:%04lX %02i/%i",
sd_info.label,
sd_api_get_fs_type_text(sd_info.fs_type),
sd_info.kb_total,
sd_info.kb_free);
sd_info.kb_free,
sd_cid.ManufacturerID,
sd_cid.OEM_AppliID,
sd_cid.ProdName,
sd_cid.ProdRev >> 4,
sd_cid.ProdRev & 0xf,
sd_cid.ProdSN,
sd_cid.ManufactMonth,
sd_cid.ManufactYear + 2000);
dialog_ex_set_text(
dialog_ex, furi_string_get_cstr(app->text_string), 4, 4, AlignLeft, AlignTop);
dialog_ex, furi_string_get_cstr(app->text_string), 4, 1, AlignLeft, AlignTop);
}
view_dispatcher_switch_to_view(app->view_dispatcher, StorageSettingsViewDialogEx);

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 KiB

After

Width:  |  Height:  |  Size: 2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 KiB

After

Width:  |  Height:  |  Size: 2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 KiB

After

Width:  |  Height:  |  Size: 2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 1.4 KiB

View file

@ -1,7 +1,7 @@
Filetype: IR library file
Version: 1
# Last Updated 13th Jan, 2023
# Last Checked 19th Jan, 2023
# Last Checked 31th Jan, 2023
#
name: POWER
type: raw

View file

@ -1,7 +1,7 @@
Filetype: IR library file
Version: 1
# Last Updated 19th Jan, 2023
# Last Checked 19th Jan, 2023
# Last Updated 30th Jan, 2023
# Last Checked 31th Jan, 2023
#
name: POWER
type: parsed
@ -2014,3 +2014,21 @@ type: parsed
protocol: NEC
address: 00 00 00 00
command: 47 00 00 00
#
name: POWER
type: parsed
protocol: NEC
address: 80 00 00 00
command: 02 00 00 00
#
name: VOL-
type: parsed
protocol: NEC
address: 80 00 00 00
command: 05 00 00 00
#
name: MUTE
type: parsed
protocol: NEC
address: 80 00 00 00
command: 04 00 00 00

View file

@ -1,7 +1,7 @@
Filetype: IR library file
Version: 1
# Last Updated 19th Jan, 2023
# Last Checked 19th Jan, 2023
# Last Updated 31th Jan, 2023
# Last Checked 31th Jan, 2023
#
name: POWER
type: raw
@ -68,6 +68,12 @@ type: raw
frequency: 38000
duty_cycle: 0.330000
data: 1294 371 1351 322 443 1200 1295 346 1297 373 444 1224 445 1198 1300 345 498 1170 473 1171 496 1173 471 8057 1295 372 1297 347 444 1224 1296 347 1296 349 494 1172 470 1174 1293 376 469 1174 469 1200 468 1175 468 8082 1293 351 1318 349 468 1175 1292 377 1294 349 468 1177 491 1175 1293 350 442 1226 468 1175 468 1201 467 8083 1293 350 1268 401 467 1175 1293 351 1318 350 467 1175 442 1226 1293 350 467 1177 466 1200 468 1176 441 8133 1292 351 1267 400 468 1175 1292 351 1292 376 466 1177 467 1202 1291 352 442 1202 465 1203 441 1201 442 8132 1267 376 1267 402 440 1202 1266 377 1290 379 439 1226 416 1253 1241 402 415 1228 439 1229 414 1228 415 8161 1239 403 1240 404 439 1229 1240 403 1240 429 414 1229 414 1231 1264 404 413 1229 414 1255 413 1229 413 8161 1238 404 1239 405 437 1230 1239 404 1238 430 412 1230 412 1232 1262 405 412 1231 412 1256 412 1231 412 8162 1238 406 1237 406 411 1258 1237 406 1237 431 412 1256 387 1256 1213 456 386 1256 386 1257 412 1256 386 8164 1237 431 1212 431 386 1283 1212 431 1212 432 411 1257 386 1257 1211 457 386 1257 386 1258 411 1258 385 8164 1212 431 1212 432 411 1256 1213 431 1236 433 386 1256 387 1257 1238 430 386 1257 386 1282 386 1256 386
# ON/SPEED+
name: POWER
type: raw
frequency: 38000
duty_cycle: 0.330000
data: 1330 364 1385 320 523 1158 1332 365 1329 364 479 1213 481 1213 481 1213 481 1213 481 1213 1332 390 480 8072 1358 362 1329 364 478 1216 1328 366 1328 367 476 1218 476 1218 476 1218 476 1218 476 1218 1328 367 476 8103 1328 367 1327 367 476 1218 1327 367 1327 367 476 1218 476 1218 476 1218 476 1218 476 1218 1327 367 476 8103 1327 367 1327 367 476 1218 1327 368 1326 367 476 1218 476 1219 475 1219 475 1219 475 1219 1326 368 475 8104 1327 368 1326 368 475 1219 1327 368 1326 368 475 1219 475 1219 475 1219 475 1219 475 1219 1326 368 475 8105 1326 368 1326 368 475 1219 1327 368 1326 368 475 1220 474 1220 474 1220 475 1220 474 1220 1325 368 475 8105 1326 369 1325 369 474 1220 1326 369 1325 369 474 1220 474 1220 474 1220 474 1220 474 1220 1326 369 474 8106 1325 369 1325 369 474 1220 1325 369 1326 369 474 1220 474 1220 474 1220 474 1220 474 1221 1324 370 473 8132 1273 422 1272 422 446 1248 1272 422 1297 397 421 1275 419 1300 419 1276 419 1275 419 1250 1297 398 445 8135 1297 397 1297 397 447 1247 1298 397 1297 397 447 1247 447 1247 447 1247 447 1247 447 1247 1298 397 447 8134 1298 397 1298 397 447 1248 1297 397 1297 397 447 1248 446 1248 446 1248 447 1248 447 1248 1297 397 447 8133 1298 397 1297 397 447 1248 1297 397 1297 397 447 1248 446 1248 447 1248 446 1248 446 1248 1297 398 446 8134 1297 398 1296 398 446 1248 1297 398 1296 398 446 1248 446 1248 446 1248 446 1248 446 1248 1296 398 446 8134 1295 398 1296 398 446 1248 1296 398 1296 398 446 1249 445 1249 445 1249 445 1249 445 1249 1295 399 445 8134 1295 399 1295 399 445 1249 1295 399 1295 399 445 1249 445 1249 445 1250 444 1249 445 1249 1295 399 445 8135 1295 400 1294 400 444 1250 1294 400 1295 400 444 1250 444 1250 444 1250 444 1250 444 1250 1294 401 443 8137 1293 401 1294 401 442 1251 1294 401 1293 401 442 1253 442 1252 442 1252 442 1252 442 1252 1293 427 417 8140 1292 426 1268 427 417 1277 1243 452 1267 427 417 1278 417 1277 417 1278 417 1277 417 1277 1268 427 417 8163 1242 452 1242 452 417 1278 1242 453 1241 453 391 1303 391 1303 392 1303 391 1303 391 1303 1242 453 415 8165 1241 453 1241 453 391 1304 1241 453 1241 454 390 1305 389 1304 390 1304 390 1305 389 1305 1240 480 364 8216 1214 480 1214 480 363 1331 1214 480 1214 480 364 1331 363 1331 363 1331 363 1331 363 1331 1214 481 363 8218 1213 481 1213 481 363 1333 1212 508 1186 508 335 1359 335 1359 335 1359 335 1359 335 1360 1186 508 335 8247 1184 509 1185 535 307 1387 1159 536 1158 589 253 1389 306 1415 278 1416 252 1442 199 1575 1052
#
name: POWER
type: raw
@ -1215,19 +1221,7 @@ type: raw
frequency: 38000
duty_cycle: 0.330000
data: 1349 364 1321 363 490 1219 1324 364 1294 389 463 1245 1324 363 462 1219 491 1221 462 1223 461 1226 484 8019 1292 393 1292 420 433 1252 1292 393 1292 420 433 1252 1292 393 460 1252 433 1252 460 1226 459 1252 433 8021 1317 393 1292 394 459 1226 1318 393 1292 394 458 1226 1318 394 432 1253 459 1226 459 1253 432 1253 459
#
name: TEMP+
type: raw
frequency: 38000
duty_cycle: 0.330000
data: 1355 364 1348 363 462 1189 1355 363 1322 389 436 1245 492 1192 1351 389 436 1221 490 1197 487 1249 434 8019 1319 393 1292 393 459 1252 1292 393 1292 393 459 1252 432 1252 1292 393 459 1252 433 1252 433 1252 460 8020 1291 393 1292 420 432 1252 1292 393 1292 420 432 1253 459 1226 1291 420 432 1253 459 1226 459 1252 432
#
name: TEMP-
type: raw
frequency: 38000
duty_cycle: 0.330000
data: 1354 363 1322 363 489 1219 1325 364 1295 389 463 1245 464 1219 466 1219 1324 416 435 1223 461 1249 461 8019 1292 393 1318 393 432 1252 1292 393 1318 393 433 1252 460 1225 460 1252 1292 393 460 1225 459 1252 433 8020 1318 393 1292 393 459 1253 1291 393 1291 394 459 1253 431 1253 432 1253 1291 420 432 1253 432 1253 458
#
#
name: TIMER
type: raw
frequency: 38000
@ -1323,3 +1317,51 @@ type: raw
frequency: 38000
duty_cycle: 0.330000
data: 2222 685 781 1419 808 1419 780 686 755 1447 781 684 783 686 780 686 807 1470 757 1470 756 1471 756 1497 756 1445 780 1447 780 1421 779 1421 754 101509 2250 684 777 1421 781 1444 754 687 754 1472 779 687 779 688 753 688 830 1448 777 1449 778 1448 752 1501 752 1450 773 1451 778 1422 778 1423 777
#
name: POWER
type: parsed
protocol: NECext
address: 41 59 00 00
command: 05 FA 00 00
#
name: SPEED+
type: parsed
protocol: NECext
address: 41 59 00 00
command: 44 BB 00 00
# OFF
name: POWER
type: raw
frequency: 38000
duty_cycle: 0.330000
data: 1332 391 1303 359 485 1241 1304 391 1303 391 452 1242 452 1242 452 1243 451 1243 452 1243 452 1242 1303 7275 1303 391 1303 390 453 1240 1330 364 1328 366 477 1218 476 1218 476 1220 474 1220 474 1220 474 1220 1325 7252 1326 369 1325 369 475 1219 1325 369 1325 369 475 1219 475 1219 475 1219 475 1219 475 1219 475 1219 1326 7254 1326 369 1326 369 475 1219 1326 369 1326 369 475 1219 475 1220 475 1219 475 1219 475 1220 475 1220 1325 7256 1325 370 1325 370 474 1220 1325 370 1325 370 474 1220 475 1220 474 1220 474 1220 474 1221 474 1221 1324 7281 1301 370 1325 370 474 1220 1325 370 1324 370 474 1221 474 1221 473 1221 473 1221 473 1221 474 1220 1325 7256 1325 370 1324 371 473 1221 1324 370 1324 370 474 1221 473 1221 473 1220 474 1220 474 1220 474 1220 1324 7256 1324 371 1323 371 473 1221 1323 371 1323 371 473 1221 473 1221 473 1221 473 1221 473 1221 473 1221 1323 7256 1323 371 1323 371 473 1222 1322 372 1322 372 472 1222 472 1222 472 1222 472 1222 472 1222 472 1222 1322 7281 1297 372 1322 373 471 1246 1298 396 1298 397 447 1247 447 1247 447 1247 447 1247 447 1247 447 1247 1297 7281 1297 397 1297 397 447 1247 1297 397 1297 397 447 1247 447 1247 447 1247 447 1247 447 1248 446 1248 1296 7282 1296 398 1296 398 446 1248 1296 398 1296 398 447 1248 446 1248 446 1248 446 1247 447 1247 447 1248 1295 7281 1296 398 1296 398 446 1248 1296 399 1295 399 446 1248 446 1248 446 1248 446 1248 446 1248 446 1248 1296 7282 1296 399 1295 399 445 1248 1295 399 1295 399 445 1248 446 1249 444 1249 445 1249 445 1249 445 1249 1294 7282 1294 399 1295 400 444 1249 1295 399 1294 400 444 1250 444 1249 445 1250 444 1249 445 1250 444 1250 1294 7282 1294 400 1294 400 444 1250 1294 400 1294 401 443 1251 443 1250 444 1250 444 1250 444 1250 443 1251 1292 7284 1292 401 1293 402 442 1251 1292 402 1291 403 442 1276 418 1252 442 1276 418 1277 417 1277 417 1277 1242 7334 1268 427 1242 452 392 1302 1242 452 1242 452 392 1302 392 1302 392 1302 392 1302 391 1302 392 1302 1242 7335 1241 453 1240 453 391 1302 1241 453 1240 453 391 1303 390 1303 390 1303 390 1304 390 1303 391 1304 1240 7361 1216 479 1216 479 364 1330 1215 479 1215 480 363 1330 364 1330 364 1330 364 1330 364 1330 364 1330 1214 7362 1213 480 1214 480 363 1331 1213 481 1213 482 361 1332 362 1332 362 1331 363 1331 362 1332 362 1332 1212 7391 1185 508 1185 509 334 1359 1185 509 1184 509 334 1359 335 1359 334 1386 307 1386 307 1386 307 1386 1159 7445 1131 562 1131 616 196 1471 1104
#
name: POWER
type: raw
frequency: 38000
duty_cycle: 0.330000
data: 2265 691 792 690 792 684 793 1342 821 1310 821 640 822 634 822 660 792 718 791 690 791 685 791 680 791 675 791 670 790 1330 789 662 788 99517 2230 723 760 721 760 715 761 1374 761 1368 762 699 762 694 762 689 762 747 761 719 761 715 761 710 761 705 761 701 760 1360 760 691 760
#
name: SPEED+
type: raw
frequency: 38000
duty_cycle: 0.330000
data: 2312 617 867 641 840 577 899 1269 866 1265 865 622 839 593 864 612 838 670 839 1306 839 611 865 1271 864 627 839 1259 865 590 866 611 840 102129 2316 670 813 667 813 664 811 1323 810 1319 759 702 759 697 759 692 759 750 759 1386 758 717 759 1376 758 706 759 1365 759 696 759 691 760
#
name: SPEED-
type: raw
frequency: 38000
duty_cycle: 0.330000
data: 2232 725 759 722 759 717 759 1375 760 1370 760 702 759 697 759 692 788 1384 788 1356 788 1351 788 1347 787 1342 787 1338 786 670 785 1330 784 99591 2229 724 760 721 785 690 785 1349 784 1345 784 677 783 673 782 669 781 1391 781 1363 781 1359 780 1353 781 1349 780 1343 781 675 780 1334 780
#
name: TIMER
type: raw
frequency: 38000
duty_cycle: 0.330000
data: 2230 724 760 720 761 715 761 1374 761 1370 760 701 760 696 760 691 786 1385 760 720 787 689 760 710 760 705 786 1339 785 670 785 665 784 98757 2224 729 754 726 755 721 754 1380 754 1375 754 706 754 701 754 696 754 1418 754 726 754 720 755 716 755 710 755 1369 755 700 755 696 755
#
name: ROTATE
type: raw
frequency: 38000
duty_cycle: 0.330000
data: 2256 698 786 695 786 689 787 1348 787 1342 787 673 788 668 787 663 787 722 786 696 784 1353 786 1375 759 706 759 702 783 672 783 1332 782 102265 2310 645 838 668 812 664 811 1323 810 1319 810 651 809 647 808 642 808 701 807 673 807 1332 807 1327 807 658 808 653 807 648 807 1307 807

View file

@ -1,7 +1,7 @@
Filetype: IR library file
Version: 1
# Last Updated 13th Jan, 2023
# Last Checked 19th Jan, 2023
# Last Updated 31th Jan, 2023
# Last Checked 31th Jan, 2023
#
# ON
name: POWER
@ -778,3 +778,27 @@ type: parsed
protocol: NEC
address: 31 00 00 00
command: 81 00 00 00
#
name: POWER
type: parsed
protocol: NECext
address: 83 F4 00 00
command: 17 E8 00 00
#
name: VOL+
type: raw
frequency: 38000
duty_cycle: 0.33
data: 9010 4413 532 1617 532 1617 533 489 533 489 533 489 558 464 558 465 557 1593 557 465 557 466 556 1594 555 467 555 1595 529 1621 554 1595 581 1569 581 441 581 1569 581 441 581 441 581 441 581 441 581 441 581 1569 581 1569 581 441 581 1569 580 1569 580 1570 580 1595 554 1595 555 468 554 42156 8983 2135 556
#
name: VOL-
type: raw
frequency: 38000
duty_cycle: 0.33
data: 9032 4390 556 1592 559 1591 559 463 559 463 558 464 557 465 556 465 557 1593 583 440 581 441 580 1569 581 441 581 1569 580 1569 581 1569 581 1570 580 1596 554 1596 554 468 554 468 554 468 554 442 580 442 580 1596 554 469 553 469 553 1596 554 1596 553 1597 550 1598 553 1598 552 469 551 42155 9008 2107 531 95218 9006 2108 582
#
name: MUTE
type: raw
frequency: 38000
duty_cycle: 0.33
data: 9011 4388 557 1617 532 1617 532 489 533 489 558 464 558 440 582 440 582 1593 556 466 556 466 556 1594 556 467 555 1595 555 1595 529 1620 554 1596 554 467 554 468 555 1595 579 443 581 1569 581 441 581 441 580 442 581 1569 581 1569 581 441 581 1569 580 441 581 1569 581 1569 581 1570 579 42152 8957 2159 556

View file

@ -1,7 +1,7 @@
Filetype: IR library file
Version: 1
# Last Updated 19th Jan, 2023
# Last Checked 19th Jan, 2023
# Last Checked 31th Jan, 2023
#
name: POWER
type: parsed

View file

@ -0,0 +1,83 @@
# Infrared Captures
**Credits go to @gsurkov, @skotopes, @knrn-ai, @DrZlo13 and @ahumeniy for making and contributing to the original `UniversalRemotes.md` Documentation located [Here](https://github.com/flipperdevices/flipperzero-firmware/blob/dev/documentation/UniversalRemotes.md).**
**slightly adapted by @amec0e**
## Televisions, Fans, Audio and Projectors
Each signal is recorded using the following process:
1. Get the remote and point it to Flipper's IR receiver.
2. Start learning a new remote if it's the first button or press `+` to add a new button otherwise.
3. Do a Quick Press of a remote button and save it under a corresponding name. **(NOTE: Don't hold the remote button down, this will result in long captures and long playbacks ultimately slowing down the universal remotes performance)**
4. Repeat steps 2-3 until all required signals are saved.
The signal names are self-explanatory. Remember to make sure that every recorded signal does what it's supposed to.
**NOTE:** It's possible some devices around you will cause interference and may force your capture into raw data instead of a parsed code.
If you notice you get a parsed code when capturing it's best to click "Retry" a few times on the flipper when capturing to ensure the device is not suffering from any interference, and that the cleanest capture is possible.
## Types of data
**Parsed data**
This is the cleanest type of data because it means it is a recognised code.
```
name: EXAMPLE
type: parsed
protocol: NEC
address: 07 00 00 00
command: 02 00 00 00
```
**Raw Data**
With raw data its important not to hold the remotes button down when capturing on your flipper as this increases not only the size of the capture but the repeats and also how long it takes to send the signal back. Below is an ideal button capture.
```
#
name: EXAMPLE
type: raw
frequency: 38000
duty_cycle: 0.330000
data: 2410 597 1189 599 592 600 1186 602 589 603 1183 606 595 597 593 598 1208 605 596 596 594 597 593 599 592 25604 2403 604 1182 606 595 597 1189 599 591 601 1185 603 618 573 617 575 1211 602 588 603 588 605 596 596 594 25605 2402 604 1192 596 594 597 1189 599 592 601 1185 628 593 598 593 600 1186 602 589 603 588 604 597 595 596
```
**Capturing Raw Data:**
If you are sure your remote is using raw data the best way to capture it will be to do a quick button press **(don't hold the remotes button down)** and look at how many samples you get, the general idea here is to get the lowest amount of raw data samples captured (around 100 samples is about right) while making sure that the playback on the device is still working. This is usually accomplished by doing a quick button press on the remote when capturing.
## Air Conditioners
Air conditioners differ from most other infrared-controlled devices because their state is tracked by the remote.
The majority of A/C remotes have a small display that shows the current mode, temperature, and other settings.
When the user presses a button, a whole set of parameters is transmitted to the device, which must be recorded and used as a whole.
In order to capture a particular air conditioner, there is a particular process require to capturing and this is done using the following process:
1. Get the remote and press the **Power Button** so that the display shows that A/C is ON.
2. Set the A/C to the corresponding mode (see table below), leaving other parameters such as fan speed or vane on **AUTO** (if applicable).
3. Press the **POWER** button to switch the A/C off.
4. Start learning a new remote on Flipper if it's the first button or press `+` to add a new button otherwise.
5. Point the remote to Flipper's IR receiver as directed and press **POWER** button once again.
6. Save the resulting signal under the specified name.
7. Repeat steps 2-6 for each signal from the table below.
| Signal | Mode | Temperature | Note |
| :-----: | :--------: | :---------: | ----------------------------------- |
| Dh | Dehumidify | N/A | |
| Cool_hi | Cooling | See note | Lowest temperature in cooling mode |
| Cool_lo | Cooling | 23°C | |
| Heat_hi | Heating | See note | Highest temperature in heating mode |
| Heat_lo | Heating | 23°C | |
Finally, record the `Off` signal:
1. Make sure the display shows that the A/C is ON.
2. Start learning a new signal on Flipper and point the remote towards the IR receiver.
3. Press the **POWER** button so that the remote shows the OFF state.
4. Save the resulting signal under the name `Off`.
Test the file against the actual device. Make sure that every signal does what it's supposed to.

View file

@ -67,6 +67,7 @@ Can be combined with a special key command or a single character.
|ALT-SHIFT|ALT+SHIFT|
|ALT-GUI|ALT+WIN|
|GUI-SHIFT|WIN+SHIFT|
|GUI-CTRL|WIN+CTRL|
## String

View file

@ -1,5 +1,5 @@
entry,status,name,type,params
Version,+,11.7,,
Version,+,11.9,,
Header,+,applications/services/bt/bt_service/bt.h,,
Header,+,applications/services/cli/cli.h,,
Header,+,applications/services/cli/cli_vcp.h,,
@ -1408,6 +1408,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,
@ -1532,6 +1533,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*
@ -1901,7 +1903,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
@ -2087,7 +2089,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*

1 entry status name type params
2 Version + 11.7 11.9
3 Header + applications/services/bt/bt_service/bt.h
4 Header + applications/services/cli/cli.h
5 Header + applications/services/cli/cli_vcp.h
1408 Function + furi_hal_vibro_on void _Bool
1409 Function - furi_init void
1410 Function + furi_kernel_get_tick_frequency uint32_t
1411 Function + furi_kernel_is_irq_or_masked _Bool
1412 Function + furi_kernel_lock int32_t
1413 Function + furi_kernel_restore_lock int32_t int32_t
1414 Function + furi_kernel_unlock int32_t
1533 Function + furi_thread_get_return_code int32_t FuriThread*
1534 Function + furi_thread_get_stack_space uint32_t FuriThreadId
1535 Function + furi_thread_get_state FuriThreadState FuriThread*
1536 Function + furi_thread_get_stdout_callback FuriThreadStdoutWriteCallback
1537 Function + furi_thread_is_suspended _Bool FuriThreadId
1538 Function + furi_thread_join _Bool FuriThread*
1539 Function + furi_thread_mark_as_service void FuriThread*
1903 Function - mf_classic_dict_is_key_present_str _Bool MfClassicDict*, FuriString*
1904 Function - mf_classic_dict_rewind _Bool MfClassicDict*
1905 Function - mf_classic_emulator _Bool MfClassicEmulator*, FuriHalNfcTxRxContext*
1906 Function - mf_classic_get_classic_type MfClassicType int8_t, uint8_t, uint8_t uint8_t, uint8_t, uint8_t
1907 Function - mf_classic_get_read_sectors_and_keys void MfClassicData*, uint8_t*, uint8_t*
1908 Function - mf_classic_get_sector_by_block uint8_t uint8_t
1909 Function - mf_classic_get_sector_trailer_block_num_by_sector uint8_t uint8_t
2089 Function - pow double double, double
2090 Function - pow10 double double
2091 Function - pow10f float float
2092 Function - + power_enable_low_battery_level_notification void Power*, _Bool
2093 Function + power_get_info void Power*, PowerInfo*
2094 Function + power_get_pubsub FuriPubSub* Power*
2095 Function + power_is_battery_healthy _Bool Power*

View file

@ -779,25 +779,10 @@ uint8_t SD_GetCIDRegister(SD_CID* Cid) {
Cid->ManufacturerID = CID_Tab[0];
/* Byte 1 */
Cid->OEM_AppliID = CID_Tab[1] << 8;
/* Byte 2 */
Cid->OEM_AppliID |= CID_Tab[2];
memcpy(Cid->OEM_AppliID, CID_Tab + 1, 2);
/* Byte 3 */
Cid->ProdName1 = CID_Tab[3] << 24;
/* Byte 4 */
Cid->ProdName1 |= CID_Tab[4] << 16;
/* Byte 5 */
Cid->ProdName1 |= CID_Tab[5] << 8;
/* Byte 6 */
Cid->ProdName1 |= CID_Tab[6];
/* Byte 7 */
Cid->ProdName2 = CID_Tab[7];
memcpy(Cid->ProdName, CID_Tab + 3, 5);
/* Byte 8 */
Cid->ProdRev = CID_Tab[8];
@ -815,11 +800,12 @@ uint8_t SD_GetCIDRegister(SD_CID* Cid) {
Cid->ProdSN |= CID_Tab[12];
/* Byte 13 */
Cid->Reserved1 |= (CID_Tab[13] & 0xF0) >> 4;
Cid->ManufactDate = (CID_Tab[13] & 0x0F) << 8;
Cid->Reserved1 = (CID_Tab[13] & 0xF0) >> 4;
Cid->ManufactYear = (CID_Tab[13] & 0x0F) << 4;
/* Byte 14 */
Cid->ManufactDate |= CID_Tab[14];
Cid->ManufactYear |= (CID_Tab[14] & 0xF0) >> 4;
Cid->ManufactMonth = (CID_Tab[14] & 0x0F);
/* Byte 15 */
Cid->CID_CRC = (CID_Tab[15] & 0xFE) >> 1;
@ -837,6 +823,21 @@ uint8_t SD_GetCIDRegister(SD_CID* Cid) {
return retr;
}
uint8_t BSP_SD_GetCIDRegister(SD_CID* Cid) {
uint8_t retr = BSP_SD_ERROR;
/* Slow speed init */
furi_hal_spi_acquire(&furi_hal_spi_bus_handle_sd_slow);
furi_hal_sd_spi_handle = &furi_hal_spi_bus_handle_sd_slow;
memset(Cid, 0, sizeof(SD_CID));
retr = SD_GetCIDRegister(Cid);
furi_hal_sd_spi_handle = NULL;
furi_hal_spi_release(&furi_hal_spi_bus_handle_sd_slow);
return retr;
}
/**
* @brief Sends 5 bytes command to the SD card and get response
* @param Cmd: The user expected command to send to SD card.

View file

@ -133,16 +133,16 @@ typedef struct {
* @brief Card Identification Data: CID Register
*/
typedef struct {
__IO uint8_t ManufacturerID; /* ManufacturerID */
__IO uint16_t OEM_AppliID; /* OEM/Application ID */
__IO uint32_t ProdName1; /* Product Name part1 */
__IO uint8_t ProdName2; /* Product Name part2*/
__IO uint8_t ProdRev; /* Product Revision */
__IO uint32_t ProdSN; /* Product Serial Number */
__IO uint8_t Reserved1; /* Reserved1 */
__IO uint16_t ManufactDate; /* Manufacturing Date */
__IO uint8_t CID_CRC; /* CID CRC */
__IO uint8_t Reserved2; /* always 1 */
uint8_t ManufacturerID; /* ManufacturerID */
char OEM_AppliID[2]; /* OEM/Application ID */
char ProdName[5]; /* Product Name */
uint8_t ProdRev; /* Product Revision */
uint32_t ProdSN; /* Product Serial Number */
uint8_t Reserved1; /* Reserved1 */
uint8_t ManufactYear; /* Manufacturing Year */
uint8_t ManufactMonth; /* Manufacturing Month */
uint8_t CID_CRC; /* CID CRC */
uint8_t Reserved2; /* always 1 */
} SD_CID;
/**
@ -207,6 +207,7 @@ uint8_t
uint8_t BSP_SD_Erase(uint32_t StartAddr, uint32_t EndAddr);
uint8_t BSP_SD_GetCardState(void);
uint8_t BSP_SD_GetCardInfo(SD_CardInfo* pCardInfo);
uint8_t BSP_SD_GetCIDRegister(SD_CID* Cid);
/* Link functions for SD Card peripheral*/
void SD_SPI_Slow_Init(void);

View file

@ -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

View file

@ -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();

View file

@ -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
*

View file

@ -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;

View file

@ -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();

View file

@ -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

View file

@ -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;

View file

@ -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)

View file

@ -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;
}

View file

@ -136,17 +136,45 @@ LevelDuration protocol_paradox_encoder_yield(ProtocolParadox* protocol) {
return level_duration_make(level, duration);
};
static uint8_t protocol_paradox_calculate_checksum(uint8_t fc, uint16_t card_id) {
uint8_t card_hi = (card_id >> 8) & 0xff;
uint8_t card_lo = card_id & 0xff;
uint8_t arr[5] = {0, 0, fc, card_hi, card_lo};
uint8_t manchester[9];
bit_lib_push_bit(manchester, 9, false);
bit_lib_push_bit(manchester, 9, false);
bit_lib_push_bit(manchester, 9, false);
bit_lib_push_bit(manchester, 9, false);
for(uint8_t i = 6; i < 40; i += 1) {
if(bit_lib_get_bit(arr, i) == 0b1) {
bit_lib_push_bit(manchester, 9, true);
bit_lib_push_bit(manchester, 9, false);
} else {
bit_lib_push_bit(manchester, 9, false);
bit_lib_push_bit(manchester, 9, true);
}
}
uint8_t output = bit_lib_crc8(manchester, 9, 0x31, 0x00, true, true, 0x06);
return output;
}
void protocol_paradox_render_data(ProtocolParadox* protocol, FuriString* result) {
uint8_t* decoded_data = protocol->data;
uint8_t fc = bit_lib_get_bits(decoded_data, 10, 8);
uint16_t card_id = bit_lib_get_bits_16(decoded_data, 18, 16);
uint8_t card_crc = bit_lib_get_bits_16(decoded_data, 34, 8);
uint8_t calc_crc = protocol_paradox_calculate_checksum(fc, card_id);
furi_string_cat_printf(result, "Facility: %u\r\n", fc);
furi_string_cat_printf(result, "Card: %u\r\n", card_id);
furi_string_cat_printf(result, "Data: ");
for(size_t i = 0; i < PARADOX_DECODED_DATA_SIZE; i++) {
furi_string_cat_printf(result, "%02X", decoded_data[i]);
}
furi_string_cat_printf(result, "CRC: %u Calc CRC: %u\r\n", card_crc, calc_crc);
if(card_crc != calc_crc) furi_string_cat_printf(result, "CRC Mismatch, Invalid Card!\r\n");
};
void protocol_paradox_render_brief_data(ProtocolParadox* protocol, FuriString* result) {
@ -154,8 +182,15 @@ void protocol_paradox_render_brief_data(ProtocolParadox* protocol, FuriString* r
uint8_t fc = bit_lib_get_bits(decoded_data, 10, 8);
uint16_t card_id = bit_lib_get_bits_16(decoded_data, 18, 16);
uint8_t card_crc = bit_lib_get_bits_16(decoded_data, 34, 8);
uint8_t calc_crc = protocol_paradox_calculate_checksum(fc, card_id);
furi_string_cat_printf(result, "FC: %03u, Card: %05u", fc, card_id);
furi_string_cat_printf(result, "FC: %03u, Card: %05u\r\n", fc, card_id);
if(calc_crc == card_crc) {
furi_string_cat_printf(result, "CRC : %03u", card_crc);
} else {
furi_string_cat_printf(result, "Card is Invalid!");
}
};
bool protocol_paradox_write_data(ProtocolParadox* protocol, void* data) {

View file

@ -352,11 +352,27 @@ void nfc_generate_mf_classic(NfcDeviceData* data, uint8_t uid_len, MfClassicType
}
// Set SAK to 08
data->nfc_data.sak = 0x08;
} else if(type == MfClassicTypeMini) {
// Set every block to 0xFF
for(uint16_t i = 1; i < MF_MINI_TOTAL_SECTORS_NUM * 4; i += 1) {
if(mf_classic_is_sector_trailer(i)) {
nfc_generate_mf_classic_sector_trailer(mfc, i);
} else {
memset(&mfc->block[i].value, 0xFF, 16);
}
mf_classic_set_block_read(mfc, i, &mfc->block[i]);
}
// Set SAK to 09
data->nfc_data.sak = 0x09;
}
mfc->type = type;
}
static void nfc_generate_mf_mini(NfcDeviceData* data) {
nfc_generate_mf_classic(data, 4, MfClassicTypeMini);
}
static void nfc_generate_mf_classic_1k_4b_uid(NfcDeviceData* data) {
nfc_generate_mf_classic(data, 4, MfClassicType1k);
}
@ -438,6 +454,11 @@ static const NfcGenerator ntag_i2c_plus_2k_generator = {
.generator_func = nfc_generate_ntag_i2c_plus_2k,
};
static const NfcGenerator mifare_mini_generator = {
.name = "Mifare Mini",
.generator_func = nfc_generate_mf_mini,
};
static const NfcGenerator mifare_classic_1k_4b_uid_generator = {
.name = "Mifare Classic 1k 4byte UID",
.generator_func = nfc_generate_mf_classic_1k_4b_uid,
@ -472,6 +493,7 @@ const NfcGenerator* const nfc_generators[] = {
&ntag_i2c_2k_generator,
&ntag_i2c_plus_1k_generator,
&ntag_i2c_plus_2k_generator,
&mifare_mini_generator,
&mifare_classic_1k_4b_uid_generator,
&mifare_classic_1k_7b_uid_generator,
&mifare_classic_4k_4b_uid_generator,

View file

@ -163,6 +163,7 @@ void reader_analyzer_stop(ReaderAnalyzer* instance) {
}
if(instance->pcap) {
nfc_debug_pcap_free(instance->pcap);
instance->pcap = NULL;
}
}

View file

@ -773,7 +773,10 @@ static bool nfc_device_save_mifare_classic_data(FlipperFormat* file, NfcDevice*
do {
if(!flipper_format_write_comment_cstr(file, "Mifare Classic specific data")) break;
if(data->type == MfClassicType1k) {
if(data->type == MfClassicTypeMini) {
if(!flipper_format_write_string_cstr(file, "Mifare Classic type", "MINI")) break;
blocks = 20;
} else if(data->type == MfClassicType1k) {
if(!flipper_format_write_string_cstr(file, "Mifare Classic type", "1K")) break;
blocks = 64;
} else if(data->type == MfClassicType4k) {
@ -871,7 +874,10 @@ static bool nfc_device_load_mifare_classic_data(FlipperFormat* file, NfcDevice*
do {
// Read Mifare Classic type
if(!flipper_format_read_string(file, "Mifare Classic type", temp_str)) break;
if(!furi_string_cmp(temp_str, "1K")) {
if(!furi_string_cmp(temp_str, "MINI")) {
data->type = MfClassicTypeMini;
data_blocks = 20;
} else if(!furi_string_cmp(temp_str, "1K")) {
data->type = MfClassicType1k;
data_blocks = 64;
} else if(!furi_string_cmp(temp_str, "4K")) {
@ -946,7 +952,9 @@ static bool nfc_device_save_mifare_classic_keys(NfcDevice* dev) {
if(!flipper_format_file_open_always(file, furi_string_get_cstr(temp_str))) break;
if(!flipper_format_write_header_cstr(file, nfc_keys_file_header, nfc_keys_file_version))
break;
if(data->type == MfClassicType1k) {
if(data->type == MfClassicTypeMini) {
if(!flipper_format_write_string_cstr(file, "Mifare Classic type", "MINI")) break;
} else if(data->type == MfClassicType1k) {
if(!flipper_format_write_string_cstr(file, "Mifare Classic type", "1K")) break;
} else if(data->type == MfClassicType4k) {
if(!flipper_format_write_string_cstr(file, "Mifare Classic type", "4K")) break;
@ -996,7 +1004,9 @@ bool nfc_device_load_key_cache(NfcDevice* dev) {
if(furi_string_cmp_str(temp_str, nfc_keys_file_header)) break;
if(version != nfc_keys_file_version) break;
if(!flipper_format_read_string(file, "Mifare Classic type", temp_str)) break;
if(!furi_string_cmp(temp_str, "1K")) {
if(!furi_string_cmp(temp_str, "MINI")) {
data->type = MfClassicTypeMini;
} else if(!furi_string_cmp(temp_str, "1K")) {
data->type = MfClassicType1k;
} else if(!furi_string_cmp(temp_str, "4K")) {
data->type = MfClassicType4k;

View file

@ -55,7 +55,9 @@ const char* nfc_mf_ul_type(MfUltralightType type, bool full_name) {
}
const char* nfc_mf_classic_type(MfClassicType type) {
if(type == MfClassicType1k) {
if(type == MfClassicTypeMini) {
return "Mifare Mini 0.3K";
} else if(type == MfClassicType1k) {
return "Mifare Classic 1K";
} else if(type == MfClassicType4k) {
return "Mifare Classic 4K";

View file

@ -142,21 +142,44 @@ static bool emv_decode_response(uint8_t* buff, uint16_t len, EmvApplication* app
success = true;
FURI_LOG_T(TAG, "found EMV_TAG_AFL %x (len=%d)", tag, tlen);
break;
case EMV_TAG_CARD_NUM: // Track 2 Equivalent Data. 0xD0 delimits PAN from expiry (YYMM)
case EMV_TAG_TRACK_1_EQUIV: {
char track_1_equiv[80];
memcpy(track_1_equiv, &buff[i], tlen);
track_1_equiv[tlen] = '\0';
success = true;
FURI_LOG_T(TAG, "found EMV_TAG_TRACK_1_EQUIV %x : %s", tag, track_1_equiv);
break;
}
case EMV_TAG_TRACK_2_EQUIV: {
// 0xD0 delimits PAN from expiry (YYMM)
for(int x = 1; x < tlen; x++) {
if(buff[i + x + 1] > 0xD0) {
memcpy(app->card_number, &buff[i], x + 1);
app->card_number_len = x + 1;
app->exp_year = (buff[i + x + 1] << 4) | (buff[i + x + 2] >> 4);
app->exp_month = (buff[i + x + 2] << 4) | (buff[i + x + 3] >> 4);
break;
}
}
// Convert 4-bit to ASCII representation
char track_2_equiv[41];
uint8_t track_2_equiv_len = 0;
for(int x = 0; x < tlen; x++) {
char top = (buff[i + x] >> 4) + '0';
char bottom = (buff[i + x] & 0x0F) + '0';
track_2_equiv[x * 2] = top;
track_2_equiv_len++;
if(top == '?') break;
track_2_equiv[x * 2 + 1] = bottom;
track_2_equiv_len++;
if(bottom == '?') break;
}
track_2_equiv[track_2_equiv_len] = '\0';
success = true;
FURI_LOG_T(
TAG,
"found EMV_TAG_CARD_NUM %x (len=%d)",
EMV_TAG_CARD_NUM,
app->card_number_len);
FURI_LOG_T(TAG, "found EMV_TAG_TRACK_2_EQUIV %x : %s", tag, track_2_equiv);
break;
}
case EMV_TAG_PAN:
memcpy(app->card_number, &buff[i], tlen);
app->card_number_len = tlen;

View file

@ -11,7 +11,8 @@
#define EMV_TAG_CARD_NAME 0x50
#define EMV_TAG_FCI 0xBF0C
#define EMV_TAG_LOG_CTRL 0x9F4D
#define EMV_TAG_CARD_NUM 0x57
#define EMV_TAG_TRACK_1_EQUIV 0x56
#define EMV_TAG_TRACK_2_EQUIV 0x57
#define EMV_TAG_PAN 0x5A
#define EMV_TAG_AFL 0x94
#define EMV_TAG_EXP_DATE 0x5F24

View file

@ -13,7 +13,9 @@
#define MF_CLASSIC_WRITE_BLOCK_CMD (0xA0)
const char* mf_classic_get_type_str(MfClassicType type) {
if(type == MfClassicType1k) {
if(type == MfClassicTypeMini) {
return "MIFARE Mini 0.3K";
} else if(type == MfClassicType1k) {
return "MIFARE Classic 1K";
} else if(type == MfClassicType4k) {
return "MIFARE Classic 4K";
@ -73,7 +75,9 @@ MfClassicSectorTrailer*
}
uint8_t mf_classic_get_total_sectors_num(MfClassicType type) {
if(type == MfClassicType1k) {
if(type == MfClassicTypeMini) {
return MF_MINI_TOTAL_SECTORS_NUM;
} else if(type == MfClassicType1k) {
return MF_CLASSIC_1K_TOTAL_SECTORS_NUM;
} else if(type == MfClassicType4k) {
return MF_CLASSIC_4K_TOTAL_SECTORS_NUM;
@ -83,7 +87,9 @@ uint8_t mf_classic_get_total_sectors_num(MfClassicType type) {
}
uint16_t mf_classic_get_total_block_num(MfClassicType type) {
if(type == MfClassicType1k) {
if(type == MfClassicTypeMini) {
return 20;
} else if(type == MfClassicType1k) {
return 64;
} else if(type == MfClassicType4k) {
return 256;
@ -361,10 +367,14 @@ bool mf_classic_check_card_type(uint8_t ATQA0, uint8_t ATQA1, uint8_t SAK) {
}
}
MfClassicType mf_classic_get_classic_type(int8_t ATQA0, uint8_t ATQA1, uint8_t SAK) {
MfClassicType mf_classic_get_classic_type(uint8_t ATQA0, uint8_t ATQA1, uint8_t SAK) {
UNUSED(ATQA1);
if((ATQA0 == 0x44 || ATQA0 == 0x04) && (SAK == 0x08 || SAK == 0x88 || SAK == 0x09)) {
return MfClassicType1k;
if((ATQA0 == 0x44 || ATQA0 == 0x04)) {
if((SAK == 0x08 || SAK == 0x88)) {
return MfClassicType1k;
} else if(SAK == 0x09) {
return MfClassicTypeMini;
}
} else if((ATQA0 == 0x01) && (ATQA1 == 0x0F) && (SAK == 0x01)) {
//skylanders support
return MfClassicType1k;
@ -595,6 +605,14 @@ void mf_classic_read_sector(FuriHalNfcTxRxContext* tx_rx, MfClassicData* data, u
if(mf_classic_read_block(tx_rx, &crypto, i, &block_tmp)) {
mf_classic_set_block_read(data, i, &block_tmp);
blocks_read++;
} else if(i > start_block) {
// Try to re-auth to read block in case prevous block was protected from read
furi_hal_nfc_sleep();
if(!mf_classic_auth(tx_rx, i, key, MfClassicKeyA, &crypto, false, 0)) break;
if(mf_classic_read_block(tx_rx, &crypto, i, &block_tmp)) {
mf_classic_set_block_read(data, i, &block_tmp);
blocks_read++;
}
}
} else {
blocks_read++;
@ -607,13 +625,20 @@ void mf_classic_read_sector(FuriHalNfcTxRxContext* tx_rx, MfClassicData* data, u
if(!key_b_found) break;
FURI_LOG_D(TAG, "Try to read blocks with key B");
key = nfc_util_bytes2num(sec_tr->key_b, sizeof(sec_tr->key_b));
furi_hal_nfc_sleep();
if(!mf_classic_auth(tx_rx, start_block, key, MfClassicKeyB, &crypto, false, 0)) break;
for(size_t i = start_block; i < start_block + total_blocks; i++) {
if(!mf_classic_is_block_read(data, i)) {
if(mf_classic_read_block(tx_rx, &crypto, i, &block_tmp)) {
mf_classic_set_block_read(data, i, &block_tmp);
blocks_read++;
} else if(i > start_block) {
// Try to re-auth to read block in case prevous block was protected from read
furi_hal_nfc_sleep();
if(!mf_classic_auth(tx_rx, i, key, MfClassicKeyB, &crypto, false, 0)) break;
if(mf_classic_read_block(tx_rx, &crypto, i, &block_tmp)) {
mf_classic_set_block_read(data, i, &block_tmp);
blocks_read++;
}
}
} else {
blocks_read++;
@ -665,6 +690,11 @@ static bool mf_classic_read_sector_with_reader(
// Read blocks
for(uint8_t i = 0; i < sector->total_blocks; i++) {
if(mf_classic_read_block(tx_rx, crypto, first_block + i, &sector->block[i])) continue;
if(i == 0) continue;
// Try to auth to read next block in case previous is locked
furi_hal_nfc_sleep();
if(!mf_classic_auth(tx_rx, first_block + i, key, key_type, crypto, false, 0)) continue;
mf_classic_read_block(tx_rx, crypto, first_block + i, &sector->block[i]);
}
// Save sector keys in last block

View file

@ -6,6 +6,7 @@
#define MF_CLASSIC_BLOCK_SIZE (16)
#define MF_CLASSIC_TOTAL_BLOCKS_MAX (256)
#define MF_MINI_TOTAL_SECTORS_NUM (5)
#define MF_CLASSIC_1K_TOTAL_SECTORS_NUM (16)
#define MF_CLASSIC_4K_TOTAL_SECTORS_NUM (40)
@ -20,6 +21,7 @@
typedef enum {
MfClassicType1k,
MfClassicType4k,
MfClassicTypeMini,
} MfClassicType;
typedef enum {
@ -94,7 +96,7 @@ const char* mf_classic_get_type_str(MfClassicType type);
bool mf_classic_check_card_type(uint8_t ATQA0, uint8_t ATQA1, uint8_t SAK);
MfClassicType mf_classic_get_classic_type(int8_t ATQA0, uint8_t ATQA1, uint8_t SAK);
MfClassicType mf_classic_get_classic_type(uint8_t ATQA0, uint8_t ATQA1, uint8_t SAK);
uint8_t mf_classic_get_total_sectors_num(MfClassicType type);