mirror of
https://github.com/DarkFlippers/unleashed-firmware.git
synced 2025-12-13 13:09:49 +04:00
Merge branch 'dev' into portasynthinca3/3922-gpio-crash
This commit is contained in:
@@ -209,6 +209,15 @@ App(
|
||||
sources=["plugins/supported_cards/hworld.c"],
|
||||
)
|
||||
|
||||
App(
|
||||
appid="trt_parser",
|
||||
apptype=FlipperAppType.PLUGIN,
|
||||
entry_point="trt_plugin_ep",
|
||||
targets=["f7"],
|
||||
requires=["nfc"],
|
||||
sources=["plugins/supported_cards/trt.c"],
|
||||
)
|
||||
|
||||
App(
|
||||
appid="nfc_start",
|
||||
targets=["f7"],
|
||||
|
||||
97
applications/main/nfc/plugins/supported_cards/trt.c
Normal file
97
applications/main/nfc/plugins/supported_cards/trt.c
Normal file
@@ -0,0 +1,97 @@
|
||||
// Flipper Zero parser for for Tianjin Railway Transit (TRT)
|
||||
// https://en.wikipedia.org/wiki/Tianjin_Metro
|
||||
// Reverse engineering and parser development by @Torron (Github: @zinongli)
|
||||
|
||||
#include "nfc_supported_card_plugin.h"
|
||||
#include <flipper_application.h>
|
||||
#include <nfc/protocols/mf_ultralight/mf_ultralight.h>
|
||||
#include <bit_lib.h>
|
||||
|
||||
#define TAG "TrtParser"
|
||||
#define LATEST_SALE_MARKER 0x02
|
||||
#define FULL_SALE_TIME_STAMP_PAGE 0x09
|
||||
#define BALANCE_PAGE 0x08
|
||||
#define SALE_RECORD_TIME_STAMP_A 0x0C
|
||||
#define SALE_RECORD_TIME_STAMP_B 0x0E
|
||||
#define SALE_YEAR_OFFSET 2000
|
||||
|
||||
static bool trt_parse(const NfcDevice* device, FuriString* parsed_data) {
|
||||
furi_assert(device);
|
||||
furi_assert(parsed_data);
|
||||
|
||||
const MfUltralightData* data = nfc_device_get_data(device, NfcProtocolMfUltralight);
|
||||
|
||||
bool parsed = false;
|
||||
|
||||
do {
|
||||
uint8_t latest_sale_page = 0;
|
||||
|
||||
// Look for sale record signature
|
||||
if(data->page[SALE_RECORD_TIME_STAMP_A].data[0] == LATEST_SALE_MARKER) {
|
||||
latest_sale_page = SALE_RECORD_TIME_STAMP_A;
|
||||
} else if(data->page[SALE_RECORD_TIME_STAMP_B].data[0] == LATEST_SALE_MARKER) {
|
||||
latest_sale_page = SALE_RECORD_TIME_STAMP_B;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
|
||||
// Check if the sale record was backed up
|
||||
const uint8_t* partial_record_pointer = &data->page[latest_sale_page - 1].data[0];
|
||||
const uint8_t* full_record_pointer = &data->page[FULL_SALE_TIME_STAMP_PAGE].data[0];
|
||||
uint32_t latest_sale_record = bit_lib_get_bits_32(partial_record_pointer, 3, 20);
|
||||
uint32_t latest_sale_full_record = bit_lib_get_bits_32(full_record_pointer, 0, 27);
|
||||
if(latest_sale_record != (latest_sale_full_record & 0xFFFFF))
|
||||
break; // check if the copy matches
|
||||
if((latest_sale_record == 0) || (latest_sale_full_record == 0))
|
||||
break; // prevent false positive
|
||||
|
||||
// Parse date
|
||||
// yyy yyyymmmm dddddhhh hhnnnnnn
|
||||
uint16_t sale_year = ((latest_sale_full_record & 0x7F00000) >> 20) + SALE_YEAR_OFFSET;
|
||||
uint8_t sale_month = (latest_sale_full_record & 0xF0000) >> 16;
|
||||
uint8_t sale_day = (latest_sale_full_record & 0xF800) >> 11;
|
||||
uint8_t sale_hour = (latest_sale_full_record & 0x7C0) >> 6;
|
||||
uint8_t sale_minute = latest_sale_full_record & 0x3F;
|
||||
|
||||
// Parse balance
|
||||
uint16_t balance = bit_lib_get_bits_16(&data->page[BALANCE_PAGE].data[2], 0, 16);
|
||||
uint16_t balance_yuan = balance / 100;
|
||||
uint8_t balance_cent = balance % 100;
|
||||
|
||||
// Format string for rendering
|
||||
furi_string_cat_printf(parsed_data, "\e#TRT Tianjin Metro\n");
|
||||
furi_string_cat_printf(parsed_data, "Single-Use Ticket\n");
|
||||
furi_string_cat_printf(parsed_data, "Balance: %u.%02u RMB\n", balance_yuan, balance_cent);
|
||||
furi_string_cat_printf(
|
||||
parsed_data,
|
||||
"Sale Date: \n%04u-%02d-%02d %02d:%02d",
|
||||
sale_year,
|
||||
sale_month,
|
||||
sale_day,
|
||||
sale_hour,
|
||||
sale_minute);
|
||||
parsed = true;
|
||||
} while(false);
|
||||
|
||||
return parsed;
|
||||
}
|
||||
|
||||
/* Actual implementation of app<>plugin interface */
|
||||
static const NfcSupportedCardsPlugin trt_plugin = {
|
||||
.protocol = NfcProtocolMfUltralight,
|
||||
.verify = NULL,
|
||||
.read = NULL,
|
||||
.parse = trt_parse,
|
||||
};
|
||||
|
||||
/* Plugin descriptor to comply with basic plugin specification */
|
||||
static const FlipperAppPluginDescriptor trt_plugin_descriptor = {
|
||||
.appid = NFC_SUPPORTED_CARD_PLUGIN_APP_ID,
|
||||
.ep_api_version = NFC_SUPPORTED_CARD_PLUGIN_API_VERSION,
|
||||
.entry_point = &trt_plugin,
|
||||
};
|
||||
|
||||
/* Plugin entry point - must return a pointer to const descriptor */
|
||||
const FlipperAppPluginDescriptor* trt_plugin_ep(void) {
|
||||
return &trt_plugin_descriptor;
|
||||
}
|
||||
@@ -999,12 +999,13 @@ static void subghz_cli_command_chat(Cli* cli, FuriString* args) {
|
||||
chat_event = subghz_chat_worker_get_event_chat(subghz_chat);
|
||||
switch(chat_event.event) {
|
||||
case SubGhzChatEventInputData:
|
||||
if(chat_event.c == CliKeyETX) {
|
||||
if(chat_event.c == CliSymbolAsciiETX) {
|
||||
printf("\r\n");
|
||||
chat_event.event = SubGhzChatEventUserExit;
|
||||
subghz_chat_worker_put_event_chat(subghz_chat, &chat_event);
|
||||
break;
|
||||
} else if((chat_event.c == CliKeyBackspace) || (chat_event.c == CliKeyDEL)) {
|
||||
} else if(
|
||||
(chat_event.c == CliSymbolAsciiBackspace) || (chat_event.c == CliSymbolAsciiDel)) {
|
||||
size_t len = furi_string_utf8_length(input);
|
||||
if(len > furi_string_utf8_length(name)) {
|
||||
printf("%s", "\e[D\e[1P");
|
||||
@@ -1026,7 +1027,7 @@ static void subghz_cli_command_chat(Cli* cli, FuriString* args) {
|
||||
}
|
||||
furi_string_set(input, sysmsg);
|
||||
}
|
||||
} else if(chat_event.c == CliKeyCR) {
|
||||
} else if(chat_event.c == CliSymbolAsciiCR) {
|
||||
printf("\r\n");
|
||||
furi_string_push_back(input, '\r');
|
||||
furi_string_push_back(input, '\n');
|
||||
@@ -1040,7 +1041,7 @@ static void subghz_cli_command_chat(Cli* cli, FuriString* args) {
|
||||
furi_string_printf(input, "%s", furi_string_get_cstr(name));
|
||||
printf("%s", furi_string_get_cstr(input));
|
||||
fflush(stdout);
|
||||
} else if(chat_event.c == CliKeyLF) {
|
||||
} else if(chat_event.c == CliSymbolAsciiLF) {
|
||||
//cut out the symbol \n
|
||||
} else {
|
||||
putc(chat_event.c, stdout);
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
#pragma once
|
||||
|
||||
#include <cli/cli.h>
|
||||
#include <cli/cli_ansi.h>
|
||||
|
||||
void subghz_on_system_start(void);
|
||||
|
||||
@@ -1,15 +1,12 @@
|
||||
#include "cli_i.h"
|
||||
#include "cli_commands.h"
|
||||
#include "cli_vcp.h"
|
||||
#include "cli_ansi.h"
|
||||
#include <furi_hal_version.h>
|
||||
#include <loader/loader.h>
|
||||
|
||||
#define TAG "CliSrv"
|
||||
|
||||
#define CLI_INPUT_LEN_LIMIT 256
|
||||
#define CLI_PROMPT ">: " // qFlipper does not recognize us if we use escape sequences :(
|
||||
#define CLI_PROMPT_LENGTH 3 // printable characters
|
||||
|
||||
Cli* cli_alloc(void) {
|
||||
Cli* cli = malloc(sizeof(Cli));
|
||||
@@ -88,7 +85,7 @@ bool cli_cmd_interrupt_received(Cli* cli) {
|
||||
char c = '\0';
|
||||
if(cli_is_connected(cli)) {
|
||||
if(cli->session->rx((uint8_t*)&c, 1, 0) == 1) {
|
||||
return c == CliKeyETX;
|
||||
return c == CliSymbolAsciiETX;
|
||||
}
|
||||
} else {
|
||||
return true;
|
||||
@@ -105,8 +102,7 @@ void cli_print_usage(const char* cmd, const char* usage, const char* arg) {
|
||||
}
|
||||
|
||||
void cli_motd(void) {
|
||||
printf(ANSI_FLIPPER_BRAND_ORANGE
|
||||
"\r\n"
|
||||
printf("\r\n"
|
||||
" _.-------.._ -,\r\n"
|
||||
" .-\"```\"--..,,_/ /`-, -, \\ \r\n"
|
||||
" .:\" /:/ /'\\ \\ ,_..., `. | |\r\n"
|
||||
@@ -120,11 +116,12 @@ void cli_motd(void) {
|
||||
" _L_ _ ___ ___ ___ ___ ____--\"`___ _ ___\r\n"
|
||||
"| __|| | |_ _|| _ \\| _ \\| __|| _ \\ / __|| | |_ _|\r\n"
|
||||
"| _| | |__ | | | _/| _/| _| | / | (__ | |__ | |\r\n"
|
||||
"|_| |____||___||_| |_| |___||_|_\\ \\___||____||___|\r\n" ANSI_RESET
|
||||
"\r\n" ANSI_FG_BR_WHITE "Welcome to " ANSI_FLIPPER_BRAND_ORANGE
|
||||
"Flipper Zero" ANSI_FG_BR_WHITE " Command Line Interface!\r\n"
|
||||
"|_| |____||___||_| |_| |___||_|_\\ \\___||____||___|\r\n"
|
||||
"\r\n"
|
||||
"Welcome to Flipper Zero Command Line Interface!\r\n"
|
||||
"Read the manual: https://docs.flipper.net/development/cli\r\n"
|
||||
"Run `help` or `?` to list available commands\r\n" ANSI_RESET "\r\n");
|
||||
"Run `help` or `?` to list available commands\r\n"
|
||||
"\r\n");
|
||||
|
||||
const Version* firmware_version = furi_hal_version_get_firmware_version();
|
||||
if(firmware_version) {
|
||||
@@ -145,7 +142,7 @@ void cli_nl(Cli* cli) {
|
||||
|
||||
void cli_prompt(Cli* cli) {
|
||||
UNUSED(cli);
|
||||
printf("\r\n" CLI_PROMPT "%s", furi_string_get_cstr(cli->line));
|
||||
printf("\r\n>: %s", furi_string_get_cstr(cli->line));
|
||||
fflush(stdout);
|
||||
}
|
||||
|
||||
@@ -168,7 +165,7 @@ static void cli_handle_backspace(Cli* cli) {
|
||||
|
||||
cli->cursor_position--;
|
||||
} else {
|
||||
cli_putc(cli, CliKeyBell);
|
||||
cli_putc(cli, CliSymbolAsciiBell);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -244,7 +241,7 @@ static void cli_handle_enter(Cli* cli) {
|
||||
printf(
|
||||
"`%s` command not found, use `help` or `?` to list all available commands",
|
||||
furi_string_get_cstr(command));
|
||||
cli_putc(cli, CliKeyBell);
|
||||
cli_putc(cli, CliSymbolAsciiBell);
|
||||
}
|
||||
|
||||
cli_reset(cli);
|
||||
@@ -308,85 +305,8 @@ static void cli_handle_autocomplete(Cli* cli) {
|
||||
cli_prompt(cli);
|
||||
}
|
||||
|
||||
typedef enum {
|
||||
CliCharClassWord,
|
||||
CliCharClassSpace,
|
||||
CliCharClassOther,
|
||||
} CliCharClass;
|
||||
|
||||
/**
|
||||
* @brief Determines the class that a character belongs to
|
||||
*
|
||||
* The return value of this function should not be used on its own; it should
|
||||
* only be used for comparing it with other values returned by this function.
|
||||
* This function is used internally in `cli_skip_run`.
|
||||
*/
|
||||
static CliCharClass cli_char_class(char c) {
|
||||
if((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || (c >= '0' && c <= '9') || c == '_') {
|
||||
return CliCharClassWord;
|
||||
} else if(c == ' ') {
|
||||
return CliCharClassSpace;
|
||||
} else {
|
||||
return CliCharClassOther;
|
||||
}
|
||||
}
|
||||
|
||||
typedef enum {
|
||||
CliSkipDirectionLeft,
|
||||
CliSkipDirectionRight,
|
||||
} CliSkipDirection;
|
||||
|
||||
/**
|
||||
* @brief Skips a run of a class of characters
|
||||
*
|
||||
* @param string Input string
|
||||
* @param original_pos Position to start the search at
|
||||
* @param direction Direction in which to perform the search
|
||||
* @returns The position at which the run ends
|
||||
*/
|
||||
static size_t cli_skip_run(FuriString* string, size_t original_pos, CliSkipDirection direction) {
|
||||
if(furi_string_size(string) == 0) return original_pos;
|
||||
if(direction == CliSkipDirectionLeft && original_pos == 0) return original_pos;
|
||||
if(direction == CliSkipDirectionRight && original_pos == furi_string_size(string))
|
||||
return original_pos;
|
||||
|
||||
int8_t look_offset = (direction == CliSkipDirectionLeft) ? -1 : 0;
|
||||
int8_t increment = (direction == CliSkipDirectionLeft) ? -1 : 1;
|
||||
int32_t position = original_pos;
|
||||
CliCharClass start_class =
|
||||
cli_char_class(furi_string_get_char(string, position + look_offset));
|
||||
|
||||
while(true) {
|
||||
position += increment;
|
||||
if(position < 0) break;
|
||||
if(position >= (int32_t)furi_string_size(string)) break;
|
||||
if(cli_char_class(furi_string_get_char(string, position + look_offset)) != start_class)
|
||||
break;
|
||||
}
|
||||
|
||||
return MAX(0, position);
|
||||
}
|
||||
|
||||
void cli_process_input(Cli* cli) {
|
||||
CliKeyCombo combo = cli_read_ansi_key_combo(cli);
|
||||
FURI_LOG_T(TAG, "code=0x%02x, mod=0x%x\r\n", combo.key, combo.modifiers);
|
||||
|
||||
if(combo.key == CliKeyTab) {
|
||||
cli_handle_autocomplete(cli);
|
||||
|
||||
} else if(combo.key == CliKeySOH) {
|
||||
furi_delay_ms(33); // We are too fast, Minicom is not ready yet
|
||||
cli_motd();
|
||||
cli_prompt(cli);
|
||||
|
||||
} else if(combo.key == CliKeyETX) {
|
||||
cli_reset(cli);
|
||||
cli_prompt(cli);
|
||||
|
||||
} else if(combo.key == CliKeyEOT) {
|
||||
cli_reset(cli);
|
||||
|
||||
} else if(combo.key == CliKeyUp && combo.modifiers == CliModKeyNo) {
|
||||
static void cli_handle_escape(Cli* cli, char c) {
|
||||
if(c == 'A') {
|
||||
// Use previous command if line buffer is empty
|
||||
if(furi_string_size(cli->line) == 0 && furi_string_cmp(cli->line, cli->last_line) != 0) {
|
||||
// Set line buffer and cursor position
|
||||
@@ -395,85 +315,67 @@ void cli_process_input(Cli* cli) {
|
||||
// Show new line to user
|
||||
printf("%s", furi_string_get_cstr(cli->line));
|
||||
}
|
||||
|
||||
} else if(combo.key == CliKeyDown && combo.modifiers == CliModKeyNo) {
|
||||
// Clear input buffer
|
||||
furi_string_reset(cli->line);
|
||||
cli->cursor_position = 0;
|
||||
printf("\r" CLI_PROMPT "\e[0K");
|
||||
|
||||
} else if(combo.key == CliKeyRight && combo.modifiers == CliModKeyNo) {
|
||||
// Move right
|
||||
} else if(c == 'B') {
|
||||
} else if(c == 'C') {
|
||||
if(cli->cursor_position < furi_string_size(cli->line)) {
|
||||
cli->cursor_position++;
|
||||
printf("\e[C");
|
||||
}
|
||||
|
||||
} else if(combo.key == CliKeyLeft && combo.modifiers == CliModKeyNo) {
|
||||
// Move left
|
||||
} else if(c == 'D') {
|
||||
if(cli->cursor_position > 0) {
|
||||
cli->cursor_position--;
|
||||
printf("\e[D");
|
||||
}
|
||||
}
|
||||
fflush(stdout);
|
||||
}
|
||||
|
||||
} else if(combo.key == CliKeyHome && combo.modifiers == CliModKeyNo) {
|
||||
// Move to beginning of line
|
||||
cli->cursor_position = 0;
|
||||
printf("\e[%uG", CLI_PROMPT_LENGTH + 1); // columns start at 1 \(-_-)/
|
||||
void cli_process_input(Cli* cli) {
|
||||
char in_chr = cli_getc(cli);
|
||||
size_t rx_len;
|
||||
|
||||
} else if(combo.key == CliKeyEnd && combo.modifiers == CliModKeyNo) {
|
||||
// Move to end of line
|
||||
cli->cursor_position = furi_string_size(cli->line);
|
||||
printf("\e[%zuG", CLI_PROMPT_LENGTH + cli->cursor_position + 1);
|
||||
|
||||
} else if(
|
||||
combo.modifiers == CliModKeyCtrl &&
|
||||
(combo.key == CliKeyLeft || combo.key == CliKeyRight)) {
|
||||
// Skip run of similar chars to the left or right
|
||||
CliSkipDirection direction = (combo.key == CliKeyLeft) ? CliSkipDirectionLeft :
|
||||
CliSkipDirectionRight;
|
||||
cli->cursor_position = cli_skip_run(cli->line, cli->cursor_position, direction);
|
||||
printf("\e[%zuG", CLI_PROMPT_LENGTH + cli->cursor_position + 1);
|
||||
|
||||
} else if(combo.key == CliKeyBackspace || combo.key == CliKeyDEL) {
|
||||
if(in_chr == CliSymbolAsciiTab) {
|
||||
cli_handle_autocomplete(cli);
|
||||
} else if(in_chr == CliSymbolAsciiSOH) {
|
||||
furi_delay_ms(33); // We are too fast, Minicom is not ready yet
|
||||
cli_motd();
|
||||
cli_prompt(cli);
|
||||
} else if(in_chr == CliSymbolAsciiETX) {
|
||||
cli_reset(cli);
|
||||
cli_prompt(cli);
|
||||
} else if(in_chr == CliSymbolAsciiEOT) {
|
||||
cli_reset(cli);
|
||||
} else if(in_chr == CliSymbolAsciiEsc) {
|
||||
rx_len = cli_read(cli, (uint8_t*)&in_chr, 1);
|
||||
if((rx_len > 0) && (in_chr == '[')) {
|
||||
cli_read(cli, (uint8_t*)&in_chr, 1);
|
||||
cli_handle_escape(cli, in_chr);
|
||||
} else {
|
||||
cli_putc(cli, CliSymbolAsciiBell);
|
||||
}
|
||||
} else if(in_chr == CliSymbolAsciiBackspace || in_chr == CliSymbolAsciiDel) {
|
||||
cli_handle_backspace(cli);
|
||||
|
||||
} else if(combo.key == CliKeyETB) { // Ctrl + Backspace
|
||||
// Delete run of similar chars to the left
|
||||
size_t run_start = cli_skip_run(cli->line, cli->cursor_position, CliSkipDirectionLeft);
|
||||
furi_string_replace_at(cli->line, run_start, cli->cursor_position - run_start, "");
|
||||
cli->cursor_position = run_start;
|
||||
printf(
|
||||
"\e[%zuG%s\e[0K\e[%zuG", // move cursor, print second half of line, erase remains, move cursor again
|
||||
CLI_PROMPT_LENGTH + cli->cursor_position + 1,
|
||||
furi_string_get_cstr(cli->line) + run_start,
|
||||
CLI_PROMPT_LENGTH + run_start + 1);
|
||||
|
||||
} else if(combo.key == CliKeyCR) {
|
||||
} else if(in_chr == CliSymbolAsciiCR) {
|
||||
cli_handle_enter(cli);
|
||||
|
||||
} else if(
|
||||
(combo.key >= 0x20 && combo.key < 0x7F) && //-V560
|
||||
(in_chr >= 0x20 && in_chr < 0x7F) && //-V560
|
||||
(furi_string_size(cli->line) < CLI_INPUT_LEN_LIMIT)) {
|
||||
if(cli->cursor_position == furi_string_size(cli->line)) {
|
||||
furi_string_push_back(cli->line, combo.key);
|
||||
cli_putc(cli, combo.key);
|
||||
furi_string_push_back(cli->line, in_chr);
|
||||
cli_putc(cli, in_chr);
|
||||
} else {
|
||||
// Insert character to line buffer
|
||||
const char in_str[2] = {combo.key, 0};
|
||||
const char in_str[2] = {in_chr, 0};
|
||||
furi_string_replace_at(cli->line, cli->cursor_position, 0, in_str);
|
||||
|
||||
// Print character in replace mode
|
||||
printf("\e[4h%c\e[4l", combo.key);
|
||||
printf("\e[4h%c\e[4l", in_chr);
|
||||
fflush(stdout);
|
||||
}
|
||||
cli->cursor_position++;
|
||||
|
||||
} else {
|
||||
cli_putc(cli, CliKeyBell);
|
||||
cli_putc(cli, CliSymbolAsciiBell);
|
||||
}
|
||||
|
||||
fflush(stdout);
|
||||
}
|
||||
|
||||
void cli_add_command(
|
||||
|
||||
@@ -10,12 +10,26 @@
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef enum {
|
||||
CliSymbolAsciiSOH = 0x01,
|
||||
CliSymbolAsciiETX = 0x03,
|
||||
CliSymbolAsciiEOT = 0x04,
|
||||
CliSymbolAsciiBell = 0x07,
|
||||
CliSymbolAsciiBackspace = 0x08,
|
||||
CliSymbolAsciiTab = 0x09,
|
||||
CliSymbolAsciiLF = 0x0A,
|
||||
CliSymbolAsciiCR = 0x0D,
|
||||
CliSymbolAsciiEsc = 0x1B,
|
||||
CliSymbolAsciiUS = 0x1F,
|
||||
CliSymbolAsciiSpace = 0x20,
|
||||
CliSymbolAsciiDel = 0x7F,
|
||||
} CliSymbols;
|
||||
|
||||
typedef enum {
|
||||
CliCommandFlagDefault = 0, /**< Default, loader lock is used */
|
||||
CliCommandFlagParallelSafe =
|
||||
(1 << 0), /**< Safe to run in parallel with other apps, loader lock is not used */
|
||||
CliCommandFlagInsomniaSafe = (1 << 1), /**< Safe to run with insomnia mode on */
|
||||
CliCommandFlagHidden = (1 << 2), /**< Not shown in `help` */
|
||||
} CliCommandFlag;
|
||||
|
||||
#define RECORD_CLI "cli"
|
||||
|
||||
@@ -1,76 +0,0 @@
|
||||
#include "cli_ansi.h"
|
||||
|
||||
/**
|
||||
* @brief Converts a single character representing a special key into the enum
|
||||
* representation
|
||||
*/
|
||||
static CliKey cli_ansi_key_from_mnemonic(char c) {
|
||||
switch(c) {
|
||||
case 'A':
|
||||
return CliKeyUp;
|
||||
case 'B':
|
||||
return CliKeyDown;
|
||||
case 'C':
|
||||
return CliKeyRight;
|
||||
case 'D':
|
||||
return CliKeyLeft;
|
||||
case 'F':
|
||||
return CliKeyEnd;
|
||||
case 'H':
|
||||
return CliKeyHome;
|
||||
default:
|
||||
return CliKeyUnrecognized;
|
||||
}
|
||||
}
|
||||
|
||||
CliKeyCombo cli_read_ansi_key_combo(Cli* cli) {
|
||||
char ch = cli_getc(cli);
|
||||
|
||||
if(ch != CliKeyEsc)
|
||||
return (CliKeyCombo){
|
||||
.modifiers = CliModKeyNo,
|
||||
.key = ch,
|
||||
};
|
||||
|
||||
ch = cli_getc(cli);
|
||||
|
||||
// ESC ESC -> ESC
|
||||
if(ch == '\e')
|
||||
return (CliKeyCombo){
|
||||
.modifiers = CliModKeyNo,
|
||||
.key = '\e',
|
||||
};
|
||||
|
||||
// ESC <char> -> Alt + <char>
|
||||
if(ch != '[')
|
||||
return (CliKeyCombo){
|
||||
.modifiers = CliModKeyAlt,
|
||||
.key = cli_getc(cli),
|
||||
};
|
||||
|
||||
ch = cli_getc(cli);
|
||||
|
||||
// ESC [ 1
|
||||
if(ch == '1') {
|
||||
// ESC [ 1 ; <modifier bitfield> <key mnemonic>
|
||||
if(cli_getc(cli) == ';') {
|
||||
CliModKey modifiers = (cli_getc(cli) - '0'); // convert following digit to a number
|
||||
modifiers &= ~1;
|
||||
return (CliKeyCombo){
|
||||
.modifiers = modifiers,
|
||||
.key = cli_ansi_key_from_mnemonic(cli_getc(cli)),
|
||||
};
|
||||
}
|
||||
|
||||
return (CliKeyCombo){
|
||||
.modifiers = CliModKeyNo,
|
||||
.key = CliKeyUnrecognized,
|
||||
};
|
||||
}
|
||||
|
||||
// ESC [ <key mnemonic>
|
||||
return (CliKeyCombo){
|
||||
.modifiers = CliModKeyNo,
|
||||
.key = cli_ansi_key_from_mnemonic(ch),
|
||||
};
|
||||
}
|
||||
@@ -1,94 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "cli.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define ANSI_RESET "\e[0m"
|
||||
#define ANSI_BOLD "\e[1m"
|
||||
#define ANSI_FAINT "\e[2m"
|
||||
|
||||
#define ANSI_FG_BLACK "\e[30m"
|
||||
#define ANSI_FG_RED "\e[31m"
|
||||
#define ANSI_FG_GREEN "\e[32m"
|
||||
#define ANSI_FG_YELLOW "\e[33m"
|
||||
#define ANSI_FG_BLUE "\e[34m"
|
||||
#define ANSI_FG_MAGENTA "\e[35m"
|
||||
#define ANSI_FG_CYAN "\e[36m"
|
||||
#define ANSI_FG_WHITE "\e[37m"
|
||||
#define ANSI_FG_BR_BLACK "\e[90m"
|
||||
#define ANSI_FG_BR_RED "\e[91m"
|
||||
#define ANSI_FG_BR_GREEN "\e[92m"
|
||||
#define ANSI_FG_BR_YELLOW "\e[93m"
|
||||
#define ANSI_FG_BR_BLUE "\e[94m"
|
||||
#define ANSI_FG_BR_MAGENTA "\e[95m"
|
||||
#define ANSI_FG_BR_CYAN "\e[96m"
|
||||
#define ANSI_FG_BR_WHITE "\e[97m"
|
||||
|
||||
#define ANSI_BG_BLACK "\e[40m"
|
||||
#define ANSI_BG_RED "\e[41m"
|
||||
#define ANSI_BG_GREEN "\e[42m"
|
||||
#define ANSI_BG_YELLOW "\e[43m"
|
||||
#define ANSI_BG_BLUE "\e[44m"
|
||||
#define ANSI_BG_MAGENTA "\e[45m"
|
||||
#define ANSI_BG_CYAN "\e[46m"
|
||||
#define ANSI_BG_WHITE "\e[47m"
|
||||
#define ANSI_BG_BR_BLACK "\e[100m"
|
||||
#define ANSI_BG_BR_RED "\e[101m"
|
||||
#define ANSI_BG_BR_GREEN "\e[102m"
|
||||
#define ANSI_BG_BR_YELLOW "\e[103m"
|
||||
#define ANSI_BG_BR_BLUE "\e[104m"
|
||||
#define ANSI_BG_BR_MAGENTA "\e[105m"
|
||||
#define ANSI_BG_BR_CYAN "\e[106m"
|
||||
#define ANSI_BG_BR_WHITE "\e[107m"
|
||||
|
||||
#define ANSI_FLIPPER_BRAND_ORANGE "\e[38;2;255;130;0m"
|
||||
|
||||
typedef enum {
|
||||
CliKeyUnrecognized = 0,
|
||||
|
||||
CliKeySOH = 0x01,
|
||||
CliKeyETX = 0x03,
|
||||
CliKeyEOT = 0x04,
|
||||
CliKeyBell = 0x07,
|
||||
CliKeyBackspace = 0x08,
|
||||
CliKeyTab = 0x09,
|
||||
CliKeyLF = 0x0A,
|
||||
CliKeyCR = 0x0D,
|
||||
CliKeyETB = 0x17,
|
||||
CliKeyEsc = 0x1B,
|
||||
CliKeyUS = 0x1F,
|
||||
CliKeySpace = 0x20,
|
||||
CliKeyDEL = 0x7F,
|
||||
|
||||
CliKeySpecial = 0x80,
|
||||
CliKeyLeft,
|
||||
CliKeyRight,
|
||||
CliKeyUp,
|
||||
CliKeyDown,
|
||||
CliKeyHome,
|
||||
CliKeyEnd,
|
||||
} CliKey;
|
||||
|
||||
typedef enum {
|
||||
CliModKeyNo = 0,
|
||||
CliModKeyAlt = 2,
|
||||
CliModKeyCtrl = 4,
|
||||
CliModKeyMeta = 8,
|
||||
} CliModKey;
|
||||
|
||||
typedef struct {
|
||||
CliModKey modifiers;
|
||||
CliKey key;
|
||||
} CliKeyCombo;
|
||||
|
||||
/**
|
||||
* @brief Reads a key or key combination
|
||||
*/
|
||||
CliKeyCombo cli_read_ansi_key_combo(Cli* cli);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@@ -1,6 +1,5 @@
|
||||
#include "cli_commands.h"
|
||||
#include "cli_command_gpio.h"
|
||||
#include "cli_ansi.h"
|
||||
|
||||
#include <core/thread.h>
|
||||
#include <furi_hal.h>
|
||||
@@ -8,10 +7,10 @@
|
||||
#include <task_control_block.h>
|
||||
#include <time.h>
|
||||
#include <notification/notification_messages.h>
|
||||
#include <notification/notification_app.h>
|
||||
#include <loader/loader.h>
|
||||
#include <lib/toolbox/args.h>
|
||||
#include <lib/toolbox/strint.h>
|
||||
#include <storage/storage.h>
|
||||
|
||||
// Close to ISO, `date +'%Y-%m-%d %H:%M:%S %u'`
|
||||
#define CLI_DATE_FORMAT "%.4d-%.2d-%.2d %.2d:%.2d:%.2d %d"
|
||||
@@ -54,196 +53,37 @@ void cli_command_info(Cli* cli, FuriString* args, void* context) {
|
||||
}
|
||||
}
|
||||
|
||||
// Lil Easter egg :>
|
||||
void cli_command_neofetch(Cli* cli, FuriString* args, void* context) {
|
||||
UNUSED(cli);
|
||||
UNUSED(args);
|
||||
UNUSED(context);
|
||||
|
||||
static const char* const neofetch_logo[] = {
|
||||
" _.-------.._ -,",
|
||||
" .-\"```\"--..,,_/ /`-, -, \\ ",
|
||||
" .:\" /:/ /'\\ \\ ,_..., `. | |",
|
||||
" / ,----/:/ /`\\ _\\~`_-\"` _;",
|
||||
" ' / /`\"\"\"'\\ \\ \\.~`_-' ,-\"'/ ",
|
||||
" | | | 0 | | .-' ,/` /",
|
||||
" | ,..\\ \\ ,.-\"` ,/` /",
|
||||
"; : `/`\"\"\\` ,/--==,/-----,",
|
||||
"| `-...| -.___-Z:_______J...---;",
|
||||
": ` _-'",
|
||||
};
|
||||
#define NEOFETCH_COLOR ANSI_FLIPPER_BRAND_ORANGE
|
||||
|
||||
// Determine logo parameters
|
||||
size_t logo_height = COUNT_OF(neofetch_logo), logo_width = 0;
|
||||
for(size_t i = 0; i < logo_height; i++)
|
||||
logo_width = MAX(logo_width, strlen(neofetch_logo[i]));
|
||||
logo_width += 4; // space between logo and info
|
||||
|
||||
// Format hostname delimiter
|
||||
const size_t size_of_hostname = 4 + strlen(furi_hal_version_get_name_ptr());
|
||||
char delimiter[64];
|
||||
memset(delimiter, '-', size_of_hostname);
|
||||
delimiter[size_of_hostname] = '\0';
|
||||
|
||||
// Get heap info
|
||||
size_t heap_total = memmgr_get_total_heap();
|
||||
size_t heap_used = heap_total - memmgr_get_free_heap();
|
||||
uint16_t heap_percent = (100 * heap_used) / heap_total;
|
||||
|
||||
// Get storage info
|
||||
Storage* storage = furi_record_open(RECORD_STORAGE);
|
||||
uint64_t ext_total, ext_free, ext_used, ext_percent;
|
||||
storage_common_fs_info(storage, "/ext", &ext_total, &ext_free);
|
||||
ext_used = ext_total - ext_free;
|
||||
ext_percent = (100 * ext_used) / ext_total;
|
||||
ext_used /= 1024 * 1024;
|
||||
ext_total /= 1024 * 1024;
|
||||
furi_record_close(RECORD_STORAGE);
|
||||
|
||||
// Get battery info
|
||||
uint16_t charge_percent = furi_hal_power_get_pct();
|
||||
const char* charge_state;
|
||||
if(furi_hal_power_is_charging()) {
|
||||
if((charge_percent < 100) && (!furi_hal_power_is_charging_done())) {
|
||||
charge_state = "charging";
|
||||
} else {
|
||||
charge_state = "charged";
|
||||
}
|
||||
} else {
|
||||
charge_state = "discharging";
|
||||
}
|
||||
|
||||
// Get misc info
|
||||
uint32_t uptime = furi_get_tick() / furi_kernel_get_tick_frequency();
|
||||
const Version* version = version_get();
|
||||
uint16_t major, minor;
|
||||
furi_hal_info_get_api_version(&major, &minor);
|
||||
|
||||
// Print ASCII art with info
|
||||
const size_t info_height = 16;
|
||||
for(size_t i = 0; i < MAX(logo_height, info_height); i++) {
|
||||
printf(NEOFETCH_COLOR "%-*s", logo_width, (i < logo_height) ? neofetch_logo[i] : "");
|
||||
switch(i) {
|
||||
case 0: // you@<hostname>
|
||||
printf("you" ANSI_RESET "@" NEOFETCH_COLOR "%s", furi_hal_version_get_name_ptr());
|
||||
break;
|
||||
case 1: // delimiter
|
||||
printf(ANSI_RESET "%s", delimiter);
|
||||
break;
|
||||
case 2: // OS: FURI <edition> <branch> <version> <commit> (SDK <maj>.<min>)
|
||||
printf(
|
||||
"OS" ANSI_RESET ": FURI %s %s %s %s (SDK %hu.%hu)",
|
||||
version_get_version(version),
|
||||
version_get_gitbranch(version),
|
||||
version_get_version(version),
|
||||
version_get_githash(version),
|
||||
major,
|
||||
minor);
|
||||
break;
|
||||
case 3: // Host: <model> <hostname>
|
||||
printf(
|
||||
"Host" ANSI_RESET ": %s %s",
|
||||
furi_hal_version_get_model_code(),
|
||||
furi_hal_version_get_device_name_ptr());
|
||||
break;
|
||||
case 4: // Kernel: FreeRTOS <maj>.<min>.<build>
|
||||
printf(
|
||||
"Kernel" ANSI_RESET ": FreeRTOS %d.%d.%d",
|
||||
tskKERNEL_VERSION_MAJOR,
|
||||
tskKERNEL_VERSION_MINOR,
|
||||
tskKERNEL_VERSION_BUILD);
|
||||
break;
|
||||
case 5: // Uptime: ?h?m?s
|
||||
printf(
|
||||
"Uptime" ANSI_RESET ": %luh%lum%lus",
|
||||
uptime / 60 / 60,
|
||||
uptime / 60 % 60,
|
||||
uptime % 60);
|
||||
break;
|
||||
case 6: // ST7567 128x64 @ 1 bpp in 1.4"
|
||||
printf("Display" ANSI_RESET ": ST7567 128x64 @ 1 bpp in 1.4\"");
|
||||
break;
|
||||
case 7: // DE: GuiSrv
|
||||
printf("DE" ANSI_RESET ": GuiSrv");
|
||||
break;
|
||||
case 8: // Shell: CliSrv
|
||||
printf("Shell" ANSI_RESET ": CliSrv");
|
||||
break;
|
||||
case 9: // CPU: STM32WB55RG @ 64 MHz
|
||||
printf("CPU" ANSI_RESET ": STM32WB55RG @ 64 MHz");
|
||||
break;
|
||||
case 10: // Memory: <used> / <total> B (??%)
|
||||
printf(
|
||||
"Memory" ANSI_RESET ": %zu / %zu B (%hu%%)", heap_used, heap_total, heap_percent);
|
||||
break;
|
||||
case 11: // Disk (/ext): <used> / <total> MiB (??%)
|
||||
printf(
|
||||
"Disk (/ext)" ANSI_RESET ": %llu / %llu MiB (%llu%%)",
|
||||
ext_used,
|
||||
ext_total,
|
||||
ext_percent);
|
||||
break;
|
||||
case 12: // Battery: ??% (<state>)
|
||||
printf("Battery" ANSI_RESET ": %hu%% (%s)" ANSI_RESET, charge_percent, charge_state);
|
||||
break;
|
||||
case 13: // empty space
|
||||
break;
|
||||
case 14: // Colors (line 1)
|
||||
for(size_t j = 30; j <= 37; j++)
|
||||
printf("\e[%dm███", j);
|
||||
break;
|
||||
case 15: // Colors (line 2)
|
||||
for(size_t j = 90; j <= 97; j++)
|
||||
printf("\e[%dm███", j);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
printf("\r\n");
|
||||
}
|
||||
printf(ANSI_RESET);
|
||||
#undef NEOFETCH_COLOR
|
||||
}
|
||||
|
||||
void cli_command_help(Cli* cli, FuriString* args, void* context) {
|
||||
UNUSED(args);
|
||||
UNUSED(context);
|
||||
printf("Commands available:");
|
||||
|
||||
// Count non-hidden commands
|
||||
CliCommandTree_it_t it_count;
|
||||
CliCommandTree_it(it_count, cli->commands);
|
||||
size_t commands_count = 0;
|
||||
while(!CliCommandTree_end_p(it_count)) {
|
||||
if(!(CliCommandTree_cref(it_count)->value_ptr->flags & CliCommandFlagHidden))
|
||||
commands_count++;
|
||||
CliCommandTree_next(it_count);
|
||||
}
|
||||
// Command count
|
||||
const size_t commands_count = CliCommandTree_size(cli->commands);
|
||||
const size_t commands_count_mid = commands_count / 2 + commands_count % 2;
|
||||
|
||||
// Create iterators starting at different positions
|
||||
const size_t columns = 3;
|
||||
const size_t commands_per_column = (commands_count / columns) + (commands_count % columns);
|
||||
CliCommandTree_it_t iterators[columns];
|
||||
for(size_t c = 0; c < columns; c++) {
|
||||
CliCommandTree_it(iterators[c], cli->commands);
|
||||
for(size_t i = 0; i < c * commands_per_column; i++)
|
||||
CliCommandTree_next(iterators[c]);
|
||||
}
|
||||
// Use 2 iterators from start and middle to show 2 columns
|
||||
CliCommandTree_it_t it_left;
|
||||
CliCommandTree_it(it_left, cli->commands);
|
||||
CliCommandTree_it_t it_right;
|
||||
CliCommandTree_it(it_right, cli->commands);
|
||||
for(size_t i = 0; i < commands_count_mid; i++)
|
||||
CliCommandTree_next(it_right);
|
||||
|
||||
// Print commands
|
||||
for(size_t r = 0; r < commands_per_column; r++) {
|
||||
// Iterate throw tree
|
||||
for(size_t i = 0; i < commands_count_mid; i++) {
|
||||
printf("\r\n");
|
||||
|
||||
for(size_t c = 0; c < columns; c++) {
|
||||
if(!CliCommandTree_end_p(iterators[c])) {
|
||||
const CliCommandTree_itref_t* item = CliCommandTree_cref(iterators[c]);
|
||||
if(!(item->value_ptr->flags & CliCommandFlagHidden)) {
|
||||
printf("%-30s", furi_string_get_cstr(*item->key_ptr));
|
||||
}
|
||||
CliCommandTree_next(iterators[c]);
|
||||
}
|
||||
// Left Column
|
||||
if(!CliCommandTree_end_p(it_left)) {
|
||||
printf("%-30s", furi_string_get_cstr(*CliCommandTree_ref(it_left)->key_ptr));
|
||||
CliCommandTree_next(it_left);
|
||||
}
|
||||
// Right Column
|
||||
if(!CliCommandTree_end_p(it_right)) {
|
||||
printf("%s", furi_string_get_cstr(*CliCommandTree_ref(it_right)->key_ptr));
|
||||
CliCommandTree_next(it_right);
|
||||
}
|
||||
};
|
||||
|
||||
if(furi_string_size(args) > 0) {
|
||||
cli_nl(cli);
|
||||
@@ -477,13 +317,24 @@ void cli_command_sysctl(Cli* cli, FuriString* args, void* context) {
|
||||
void cli_command_vibro(Cli* cli, FuriString* args, void* context) {
|
||||
UNUSED(cli);
|
||||
UNUSED(context);
|
||||
|
||||
if(!furi_string_cmp(args, "0")) {
|
||||
NotificationApp* notification = furi_record_open(RECORD_NOTIFICATION);
|
||||
notification_message_block(notification, &sequence_reset_vibro);
|
||||
furi_record_close(RECORD_NOTIFICATION);
|
||||
} else if(!furi_string_cmp(args, "1")) {
|
||||
if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagStealthMode)) {
|
||||
printf("Flipper is in stealth mode. Unmute the device to control vibration.");
|
||||
return;
|
||||
}
|
||||
|
||||
NotificationApp* notification = furi_record_open(RECORD_NOTIFICATION);
|
||||
if(notification->settings.vibro_on) {
|
||||
notification_message_block(notification, &sequence_set_vibro_on);
|
||||
} else {
|
||||
printf("Vibro is disabled in settings. Enable it to control vibration.");
|
||||
}
|
||||
|
||||
furi_record_close(RECORD_NOTIFICATION);
|
||||
} else {
|
||||
cli_print_usage("vibro", "<1|0>", furi_string_get_cstr(args));
|
||||
@@ -552,18 +403,16 @@ static void cli_command_top(Cli* cli, FuriString* args, void* context) {
|
||||
int interval = 1000;
|
||||
args_read_int_and_trim(args, &interval);
|
||||
|
||||
if(interval) printf("\e[2J\e[?25l"); // Clear display, hide cursor
|
||||
|
||||
FuriThreadList* thread_list = furi_thread_list_alloc();
|
||||
while(!cli_cmd_interrupt_received(cli)) {
|
||||
uint32_t tick = furi_get_tick();
|
||||
furi_thread_enumerate(thread_list);
|
||||
|
||||
if(interval) printf("\e[0;0f"); // Return to 0,0
|
||||
if(interval) printf("\e[2J\e[0;0f"); // Clear display and return to 0
|
||||
|
||||
uint32_t uptime = tick / furi_kernel_get_tick_frequency();
|
||||
printf(
|
||||
"\rThreads: %zu, ISR Time: %0.2f%%, Uptime: %luh%lum%lus\e[0K\r\n",
|
||||
"Threads: %zu, ISR Time: %0.2f%%, Uptime: %luh%lum%lus\r\n",
|
||||
furi_thread_list_size(thread_list),
|
||||
(double)furi_thread_list_get_isr_time(thread_list),
|
||||
uptime / 60 / 60,
|
||||
@@ -571,14 +420,14 @@ static void cli_command_top(Cli* cli, FuriString* args, void* context) {
|
||||
uptime % 60);
|
||||
|
||||
printf(
|
||||
"\rHeap: total %zu, free %zu, minimum %zu, max block %zu\e[0K\r\n\r\n",
|
||||
"Heap: total %zu, free %zu, minimum %zu, max block %zu\r\n\r\n",
|
||||
memmgr_get_total_heap(),
|
||||
memmgr_get_free_heap(),
|
||||
memmgr_get_minimum_free_heap(),
|
||||
memmgr_heap_get_max_free_block());
|
||||
|
||||
printf(
|
||||
"\r%-17s %-20s %-10s %5s %12s %6s %10s %7s %5s\e[0K\r\n",
|
||||
"%-17s %-20s %-10s %5s %12s %6s %10s %7s %5s\r\n",
|
||||
"AppID",
|
||||
"Name",
|
||||
"State",
|
||||
@@ -592,7 +441,7 @@ static void cli_command_top(Cli* cli, FuriString* args, void* context) {
|
||||
for(size_t i = 0; i < furi_thread_list_size(thread_list); i++) {
|
||||
const FuriThreadListItem* item = furi_thread_list_get_at(thread_list, i);
|
||||
printf(
|
||||
"\r%-17s %-20s %-10s %5d 0x%08lx %6lu %10lu %7zu %5.1f\e[0K\r\n",
|
||||
"%-17s %-20s %-10s %5d 0x%08lx %6lu %10lu %7zu %5.1f\r\n",
|
||||
item->app_id,
|
||||
item->name,
|
||||
item->state,
|
||||
@@ -611,8 +460,6 @@ static void cli_command_top(Cli* cli, FuriString* args, void* context) {
|
||||
}
|
||||
}
|
||||
furi_thread_list_free(thread_list);
|
||||
|
||||
if(interval) printf("\e[?25h"); // Show cursor
|
||||
}
|
||||
|
||||
void cli_command_free(Cli* cli, FuriString* args, void* context) {
|
||||
@@ -664,12 +511,6 @@ void cli_commands_init(Cli* cli) {
|
||||
cli_add_command(cli, "!", CliCommandFlagParallelSafe, cli_command_info, (void*)true);
|
||||
cli_add_command(cli, "info", CliCommandFlagParallelSafe, cli_command_info, NULL);
|
||||
cli_add_command(cli, "device_info", CliCommandFlagParallelSafe, cli_command_info, (void*)true);
|
||||
cli_add_command(
|
||||
cli,
|
||||
"neofetch",
|
||||
CliCommandFlagParallelSafe | CliCommandFlagHidden,
|
||||
cli_command_neofetch,
|
||||
NULL);
|
||||
|
||||
cli_add_command(cli, "?", CliCommandFlagParallelSafe, cli_command_help, NULL);
|
||||
cli_add_command(cli, "help", CliCommandFlagParallelSafe, cli_command_help, NULL);
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
|
||||
#include <lib/toolbox/args.h>
|
||||
#include <cli/cli.h>
|
||||
#include <cli/cli_ansi.h>
|
||||
|
||||
void crypto_cli_print_usage(void) {
|
||||
printf("Usage:\r\n");
|
||||
@@ -46,14 +45,14 @@ void crypto_cli_encrypt(Cli* cli, FuriString* args) {
|
||||
input = furi_string_alloc();
|
||||
char c;
|
||||
while(cli_read(cli, (uint8_t*)&c, 1) == 1) {
|
||||
if(c == CliKeyETX) {
|
||||
if(c == CliSymbolAsciiETX) {
|
||||
printf("\r\n");
|
||||
break;
|
||||
} else if(c >= 0x20 && c < 0x7F) {
|
||||
putc(c, stdout);
|
||||
fflush(stdout);
|
||||
furi_string_push_back(input, c);
|
||||
} else if(c == CliKeyCR) {
|
||||
} else if(c == CliSymbolAsciiCR) {
|
||||
printf("\r\n");
|
||||
furi_string_cat(input, "\r\n");
|
||||
}
|
||||
@@ -121,14 +120,14 @@ void crypto_cli_decrypt(Cli* cli, FuriString* args) {
|
||||
hex_input = furi_string_alloc();
|
||||
char c;
|
||||
while(cli_read(cli, (uint8_t*)&c, 1) == 1) {
|
||||
if(c == CliKeyETX) {
|
||||
if(c == CliSymbolAsciiETX) {
|
||||
printf("\r\n");
|
||||
break;
|
||||
} else if(c >= 0x20 && c < 0x7F) {
|
||||
putc(c, stdout);
|
||||
fflush(stdout);
|
||||
furi_string_push_back(hex_input, c);
|
||||
} else if(c == CliKeyCR) {
|
||||
} else if(c == CliSymbolAsciiCR) {
|
||||
printf("\r\n");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -523,7 +523,7 @@ int32_t desktop_srv(void* p) {
|
||||
|
||||
scene_manager_next_scene(desktop->scene_manager, DesktopSceneMain);
|
||||
|
||||
if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagLock)) {
|
||||
if(desktop_pin_code_is_set()) {
|
||||
desktop_lock(desktop);
|
||||
}
|
||||
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
#include <furi_hal.h>
|
||||
|
||||
#include <cli/cli.h>
|
||||
#include <cli/cli_ansi.h>
|
||||
#include <lib/toolbox/args.h>
|
||||
#include <lib/toolbox/dir_walk.h>
|
||||
#include <lib/toolbox/md5_calc.h>
|
||||
@@ -225,7 +224,7 @@ static void storage_cli_write(Cli* cli, FuriString* path, FuriString* args) {
|
||||
while(true) {
|
||||
uint8_t symbol = cli_getc(cli);
|
||||
|
||||
if(symbol == CliKeyETX) {
|
||||
if(symbol == CliSymbolAsciiETX) {
|
||||
size_t write_size = read_index % buffer_size;
|
||||
|
||||
if(write_size > 0) {
|
||||
|
||||
@@ -9,7 +9,7 @@ It's important to regularly update your Developer Board to ensure that you have
|
||||
|
||||
## Step 1. Install the micro Flipper Build Tool
|
||||
|
||||
is a cross-platform tool developed and supported by our team that enables basic development tasks for Flipper Zero, such as building and debugging applications, flashing firmware, creating VS Code development configurations, and flashing firmware to the Wi-Fi Developer Board.
|
||||
[micro Flipper Build Tool (uFBT)](https://pypi.org/project/ufbt/) is a cross-platform tool developed and supported by our team that enables basic development tasks for Flipper Zero, such as building and debugging applications, flashing firmware, creating VS Code development configurations, and flashing firmware to the Wi-Fi Developer Board.
|
||||
|
||||
**On Linux & macOS:**
|
||||
|
||||
@@ -43,15 +43,15 @@ To update the firmware, you need to switch your Developer Board to Bootloader mo
|
||||
- **Windows:** Go to **Device Manager** and expand the **Ports (COM & LPT)** section.
|
||||
|
||||
2. Connect the Developer Board to your computer using a USB-C cable.
|
||||
|
||||
\image html https://cdn.flipperzero.one/Flipper_Zero_Wi-Fi_devboard_update_wired_connection.jpg width=700
|
||||
|
||||
3. Switch your Developer Board to Bootloader mode:
|
||||
|
||||
1. Press and hold the **BOOT** button.
|
||||
2. Press the **RESET** button while holding the **BOOT** button.
|
||||
3. Release the **BOOT** button.
|
||||
3.1. Press and hold the **BOOT** button.
|
||||
|
||||
3.2. Press the **RESET** button while holding the **BOOT** button.
|
||||
|
||||
3.3. Release the **BOOT** button.
|
||||
\image html https://cdn.flipperzero.one/Flipper_Zero_Wi-Fi_devboard_reboot_to_bootloader.png width=700
|
||||
|
||||
4. Repeat **Step 1** and view the name of your Developer Board that appeared in the list.
|
||||
@@ -101,7 +101,6 @@ To fix it, try doing the following:
|
||||
## Step 4. Finish the installation
|
||||
|
||||
1. Reboot the Developer Board by pressing the **RESET** button.
|
||||
|
||||
\image html https://cdn.flipperzero.one/Flipper_Zero_Wi-Fi_devboard_reboot_after_flashing.jpg width=700
|
||||
|
||||
2. Disconnect and reconnect the USB-C cable.
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# Get started with the Dev Board {#dev_board_get_started}
|
||||
# Get started with the Devboard {#dev_board_get_started}
|
||||
|
||||
\image html https://cdn.flipperzero.one/Flipper_Zero_WiFi_developer_board_box_CDN.jpg width=700
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# Reading logs via the Dev Board {#dev_board_reading_logs}
|
||||
# Reading logs via the Devboard {#dev_board_reading_logs}
|
||||
|
||||
The Developer Board allows you to read Flipper Zero logs via UART. Unlike reading logs via the command-line interface (CLI), the Developer Board enables you to collect logs from the device directly to a serial console independently from the operating system of Flipper Zero. It allows you to see the device's logs when it's loading, updating, or crashing. It's useful for debugging and troubleshooting during software development.
|
||||
|
||||
|
||||
@@ -54,7 +54,6 @@ To connect the Developer Board in **Wi-Fi client** mode, you need to configure i
|
||||
4. Select the **STA** mode and enter your network's **SSID** (name) and **password**. For convenience, you can click the **+** button to see the list of nearby 2.4 GHz networks (5 GHz networks aren't supported).
|
||||
|
||||
5. Save the configuration and reboot the Developer Board.
|
||||
|
||||
\image html https://cdn.flipperzero.one/Flipper_Zero_WiFi_devboard_connect_to_WiFi_CDN.jpg width=700
|
||||
|
||||
6. Now, you can access the Devboard's web interface at [http://blackmagic.local](https://blackmagic.local) via the existing Wi-Fi network without losing connection to the internet.
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
/**
|
||||
@mainpage Overview
|
||||
@mainpage notitle
|
||||
## Welcome to Flipper Developer Documentation!
|
||||
|
||||
Welcome to the Flipper Developer Documentation!
|
||||
---
|
||||
|
||||
This documentation is intended for developers interested in modifying the Flipper Zero firmware, creating Apps and JavaScript programs, or working on external hardware modules for the device.
|
||||
|
||||
|
||||
@@ -15,6 +15,9 @@
|
||||
* Example:
|
||||
* ./fbt --extra-define=DIGITAL_SIGNAL_DEBUG_OUTPUT_PIN=gpio_ext_pb3
|
||||
*/
|
||||
#ifdef DIGITAL_SIGNAL_DEBUG_OUTPUT_PIN
|
||||
#include <furi_hal.h>
|
||||
#endif
|
||||
|
||||
#define TAG "DigitalSequence"
|
||||
|
||||
|
||||
Reference in New Issue
Block a user