Temporarily backport app updates from apps repo

This commit is contained in:
Willy-JL
2023-11-12 11:06:02 +00:00
parent 79e7f491fe
commit e309fa8a88
1498 changed files with 1325977 additions and 20227 deletions

6
.gitmodules vendored
View File

@@ -26,9 +26,6 @@
[submodule "lib/cxxheaderparser"] [submodule "lib/cxxheaderparser"]
path = lib/cxxheaderparser path = lib/cxxheaderparser
url = https://github.com/robotpy/cxxheaderparser.git url = https://github.com/robotpy/cxxheaderparser.git
[submodule "applications/external/dap_link/lib/free-dap"]
path = applications/external/dap_link/lib/free-dap
url = https://github.com/ataradov/free-dap.git
[submodule "lib/heatshrink"] [submodule "lib/heatshrink"]
path = lib/heatshrink path = lib/heatshrink
url = https://github.com/flipperdevices/heatshrink.git url = https://github.com/flipperdevices/heatshrink.git
@@ -41,6 +38,3 @@
[submodule "lib/stm32wb_copro"] [submodule "lib/stm32wb_copro"]
path = lib/stm32wb_copro path = lib/stm32wb_copro
url = https://github.com/flipperdevices/stm32wb_copro.git url = https://github.com/flipperdevices/stm32wb_copro.git
[submodule "applications/external/totp/lib/wolfssl"]
path = applications/external/totp/lib/wolfssl
url = https://github.com/wolfSSL/wolfssl.git

View File

@@ -7,9 +7,10 @@ App(
"gui", "gui",
], ],
stack_size=1 * 1024, stack_size=1 * 1024,
fap_icon="game_2048.png", order=90,
fap_icon="game_2048.png",
fap_category="Games", fap_category="Games",
fap_author="@eugene-kirzhanov", fap_author="@eugene-kirzhanov",
fap_version="1.1", fap_version="1.2",
fap_description="Play the port of the 2048 game on Flipper Zero.", fap_description="Play the port of the 2048 game on Flipper Zero.",
) )

View File

@@ -24,7 +24,8 @@
#define FRAME_TOP 1 #define FRAME_TOP 1
#define FRAME_SIZE 61 #define FRAME_SIZE 61
#define SAVING_FILENAME APP_DATA_PATH("game_2048.save") #define SAVING_DIRECTORY STORAGE_APP_DATA_PATH_PREFIX
#define SAVING_FILENAME SAVING_DIRECTORY "/game_2048.save"
typedef enum { typedef enum {
GameStateMenu, GameStateMenu,
@@ -488,8 +489,8 @@ int32_t game_2048_app() {
} }
} }
view_port_update(view_port);
furi_mutex_release(game_state->mutex); furi_mutex_release(game_state->mutex);
view_port_update(view_port);
} }
} }

View File

Before

Width:  |  Height:  |  Size: 89 B

After

Width:  |  Height:  |  Size: 89 B

View File

@@ -256,65 +256,69 @@ int32_t four_in_row_app(void* p) {
while(1) { while(1) {
// Выбираем событие из очереди в переменную event (ждем бесконечно долго, если очередь пуста) // Выбираем событие из очереди в переменную event (ждем бесконечно долго, если очередь пуста)
// и проверяем, что у нас получилось это сделать // и проверяем, что у нас получилось это сделать
furi_check(furi_message_queue_get(event_queue, &event, FuriWaitForever) == FuriStatusOk); if(furi_message_queue_get(event_queue, &event, FuriWaitForever) == FuriStatusOk) {
furi_mutex_acquire(fourinrow_state->mutex, FuriWaitForever); if((event.type == InputTypePress) && (event.key == InputKeyBack)) {
// Если нажата кнопка "назад", то выходим из цикла, а следовательно и из приложения
if(wincheck() != -1) {
notification_message(notification, &end);
furi_delay_ms(1000);
if(wincheck() == 1) {
scoreX++;
}
if(wincheck() == 2) {
scoreO++;
}
init();
}
if(event.type == InputTypePress) {
if(event.key == InputKeyOk) {
int nh = next_height(cursorx);
if(nh != -1) {
matrix[nh][cursorx] = player;
player = 3 - player;
}
}
if(event.key == InputKeyUp) {
//cursory--;
}
if(event.key == InputKeyDown) {
//cursory++;
}
if(event.key == InputKeyLeft) {
if(cursorx > 0) {
cursorx--;
}
}
if(event.key == InputKeyRight) {
if(cursorx < 6) {
cursorx++;
}
}
if(event.key == InputKeyBack) {
break; break;
} }
furi_mutex_acquire(fourinrow_state->mutex, FuriWaitForever);
if(wincheck() != -1) {
notification_message(notification, &end);
furi_delay_ms(1000);
if(wincheck() == 1) {
scoreX++;
}
if(wincheck() == 2) {
scoreO++;
}
init();
furi_mutex_release(fourinrow_state->mutex);
continue;
}
if(event.type == InputTypePress) {
if(event.key == InputKeyOk) {
int nh = next_height(cursorx);
if(nh != -1) {
matrix[nh][cursorx] = player;
player = 3 - player;
}
}
if(event.key == InputKeyUp) {
//cursory--;
}
if(event.key == InputKeyDown) {
//cursory++;
}
if(event.key == InputKeyLeft) {
if(cursorx > 0) {
cursorx--;
}
}
if(event.key == InputKeyRight) {
if(cursorx < 6) {
cursorx++;
}
}
}
furi_mutex_release(fourinrow_state->mutex);
} }
view_port_update(view_port); view_port_update(view_port);
furi_mutex_release(fourinrow_state->mutex);
} }
// Чистим созданные объекты, связанные с интерфейсом
view_port_enabled_set(view_port, false);
gui_remove_view_port(gui, view_port);
view_port_free(view_port);
furi_message_queue_free(event_queue);
furi_record_close(RECORD_GUI);
// Clear notification // Clear notification
notification_message_block(notification, &sequence_display_backlight_enforce_auto); notification_message_block(notification, &sequence_display_backlight_enforce_auto);
furi_record_close(RECORD_NOTIFICATION); furi_record_close(RECORD_NOTIFICATION);
// Специальная очистка памяти, занимаемой очередью
furi_message_queue_free(event_queue);
// Чистим созданные объекты, связанные с интерфейсом
gui_remove_view_port(gui, view_port);
view_port_free(view_port);
furi_mutex_free(fourinrow_state->mutex); furi_mutex_free(fourinrow_state->mutex);
furi_record_close(RECORD_GUI);
free(fourinrow_state); free(fourinrow_state);
return 0; return 0;

View File

@@ -7,10 +7,11 @@ App(
"gui", "gui",
], ],
stack_size=1 * 1024, stack_size=1 * 1024,
order=90,
fap_icon="4inrow_10px.png", fap_icon="4inrow_10px.png",
fap_category="Games", fap_category="Games",
fap_author="leo-need-more-coffee", fap_author="leo-need-more-coffee",
fap_weburl="https://github.com/leo-need-more-coffee/flipperzero-4inrow", fap_weburl="https://github.com/leo-need-more-coffee/flipperzero-4inrow",
fap_version="1.0", fap_version="1.1",
fap_description="4 in row Game", fap_description="4 in row Game",
) )

View File

@@ -1,13 +0,0 @@
# For details & more options, see documentation/AppManifests.md in firmware repo
App(
appid="wifisniffer", # Must be unique
name="[ESP32GPS] Wifi Sniff", # Displayed in menus
apptype=FlipperAppType.EXTERNAL,
entry_point="wifisniffer_app",
stack_size=2 * 1024,
fap_category="WiFi",
fap_icon="sniff.png", # 10x10 1-bit PNG
fap_icon_assets="assets",
fap_icon_assets_symbol="wifisniffer",
)

View File

@@ -7,7 +7,8 @@
#include "util/vector.h" #include "util/vector.h"
#define CALIBRATION_DATA_VER (1) #define CALIBRATION_DATA_VER (1)
#define CALIBRATION_DATA_PATH EXT_PATH("apps_data/air_mouse/calibration.data") #define CALIBRATION_DATA_FILE_NAME "calibration.data"
#define CALIBRATION_DATA_PATH EXT_PATH("apps_data/air_mouse/" CALIBRATION_DATA_FILE_NAME)
#define CALIBRATION_DATA_MAGIC (0x23) #define CALIBRATION_DATA_MAGIC (0x23)
#define CALIBRATION_DATA_SAVE(x) \ #define CALIBRATION_DATA_SAVE(x) \

View File

@@ -34,4 +34,4 @@ private:
} // namespace cardboard } // namespace cardboard
#endif // CARDBOARD_SDK_UTIL_MATRIX_4X4_H_ #endif // CARDBOARD_SDK_UTIL_MATRIX4X4_H_

View File

@@ -13,8 +13,7 @@
#include <math.h> #include <math.h>
#include <notification/notification.h> #include <notification/notification.h>
#include <notification/notification_messages.h> #include <notification/notification_messages.h>
#include "asteroids_icons.h" #include <asteroids_icons.h>
#include <assets_icons.h>
#define TAG "Asteroids" // Used for logging #define TAG "Asteroids" // Used for logging
#define DEBUG_MSG 0 #define DEBUG_MSG 0
@@ -29,7 +28,8 @@
#define MAXPOWERUPS 3 /* Max powerups allowed on screen */ #define MAXPOWERUPS 3 /* Max powerups allowed on screen */
#define POWERUPSTTL 400 /* Max powerup time to live, in ticks. */ #define POWERUPSTTL 400 /* Max powerup time to live, in ticks. */
#define SHIP_HIT_ANIMATION_LEN 15 #define SHIP_HIT_ANIMATION_LEN 15
#define SAVING_FILENAME APP_DATA_PATH("game_asteroids.save") #define SAVING_DIRECTORY STORAGE_APP_DATA_PATH_PREFIX
#define SAVING_FILENAME SAVING_DIRECTORY "/game_asteroids.save"
#ifndef PI #ifndef PI
#define PI 3.14159265358979f #define PI 3.14159265358979f
#endif #endif

View File

@@ -6,11 +6,12 @@ App(
cdefines=["APP_ASTEROIDS"], cdefines=["APP_ASTEROIDS"],
requires=["gui"], requires=["gui"],
stack_size=8 * 1024, stack_size=8 * 1024,
order=50,
fap_icon="appicon.png", fap_icon="appicon.png",
fap_icon_assets="assets", fap_icon_assets="assets",
fap_category="Games", fap_category="Games",
fap_author="@antirez & @SimplyMinimal", fap_author="@antirez & @SimplyMinimal",
fap_weburl="https://github.com/antirez/flipper-asteroids", fap_weburl="https://github.com/antirez/flipper-asteroids",
fap_version="1.0", fap_version="1.1",
fap_description="Asteroids game", fap_description="Asteroids game",
) )

View File

@@ -0,0 +1,13 @@
App(
appid="flipper_atomicdiceroller",
name="[J305] Atomic Dice Roller",
apptype=FlipperAppType.EXTERNAL,
entry_point="flipper_atomicdiceroller_app",
cdefines=["APP_ATOMICDICEROLLER"],
requires=[
"gui",
],
stack_size=2 * 1024,
fap_icon="atomicdiceroller.png",
fap_category="GPIO",
)

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.2 KiB

View File

@@ -0,0 +1,349 @@
// CC0 1.0 Universal (CC0 1.0)
// Public Domain Dedication
// https://github.com/nmrr
#include <stdio.h>
#include <furi.h>
#include <gui/gui.h>
#include <input/input.h>
#include <notification/notification_messages.h>
#include <furi_hal_power.h>
#include <locale/locale.h>
#include <toolbox/crc32_calc.h>
#include <lib/toolbox/md5.h>
#define SCREEN_SIZE_X 128
#define SCREEN_SIZE_Y 64
typedef enum {
EventTypeInput,
ClockEventTypeTick,
ClockEventTypeTickPause,
EventGPIO,
} EventType;
typedef struct {
EventType type;
InputEvent input;
} EventApp;
#define lineArraySize 128
typedef struct {
FuriMutex* mutex;
uint32_t cps;
uint32_t diceAvailiable;
uint8_t dice;
uint8_t method;
uint8_t pause;
} mutexStruct;
static void draw_callback(Canvas* canvas, void* ctx) {
mutexStruct* mutexVal = ctx;
mutexStruct mutexDraw;
furi_mutex_acquire(mutexVal->mutex, FuriWaitForever);
memcpy(&mutexDraw, mutexVal, sizeof(mutexStruct));
furi_mutex_release(mutexVal->mutex);
canvas_set_font(canvas, FontPrimary);
char buffer[32];
snprintf(buffer, sizeof(buffer), "%ld cps", mutexDraw.cps);
canvas_draw_str_aligned(canvas, 0, 10, AlignLeft, AlignBottom, buffer);
snprintf(buffer, sizeof(buffer), "%lu/64", mutexDraw.diceAvailiable);
canvas_draw_str_aligned(canvas, SCREEN_SIZE_X, 10, AlignRight, AlignBottom, buffer);
if(mutexDraw.method == 0)
canvas_draw_str_aligned(canvas, 0, 20, AlignLeft, AlignBottom, "Hash: CRC32");
else
canvas_draw_str_aligned(canvas, 0, 20, AlignLeft, AlignBottom, "Hash: MD5");
if(mutexDraw.dice != 0 && mutexDraw.pause == 0) {
canvas_set_font(canvas, FontBigNumbers);
snprintf(buffer, sizeof(buffer), "%u", mutexDraw.dice);
canvas_draw_str_aligned(canvas, SCREEN_SIZE_X / 2, 50, AlignCenter, AlignBottom, buffer);
}
}
static void input_callback(InputEvent* input_event, void* ctx) {
furi_assert(ctx);
FuriMessageQueue* event_queue = ctx;
EventApp event = {.type = EventTypeInput, .input = *input_event};
furi_message_queue_put(event_queue, &event, FuriWaitForever);
}
static void clock_tick(void* ctx) {
furi_assert(ctx);
FuriMessageQueue* queue = ctx;
EventApp event = {.type = ClockEventTypeTick};
furi_message_queue_put(queue, &event, 0);
}
static void clock_tick_pause(void* ctx) {
furi_assert(ctx);
FuriMessageQueue* queue = ctx;
EventApp event = {.type = ClockEventTypeTickPause};
furi_message_queue_put(queue, &event, 0);
}
static void gpiocallback(void* ctx) {
furi_assert(ctx);
FuriMessageQueue* queue = ctx;
EventApp event = {.type = EventGPIO};
furi_message_queue_put(queue, &event, 0);
}
int32_t flipper_atomicdiceroller_app() {
furi_hal_bus_enable(FuriHalBusTIM2);
LL_TIM_SetCounterMode(TIM2, LL_TIM_COUNTERMODE_UP);
LL_TIM_SetClockDivision(TIM2, LL_TIM_CLOCKDIVISION_DIV1);
LL_TIM_SetPrescaler(TIM2, 0);
LL_TIM_SetAutoReload(TIM2, 0xFFFFFFFF);
LL_TIM_SetCounter(TIM2, 0);
LL_TIM_EnableCounter(TIM2);
EventApp event;
FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(EventApp));
furi_hal_gpio_init(&gpio_ext_pa7, GpioModeInterruptFall, GpioPullUp, GpioSpeedVeryHigh);
mutexStruct mutexVal;
mutexVal.cps = 0;
mutexVal.dice = 0;
mutexVal.diceAvailiable = 0;
mutexVal.method = 0;
uint32_t counter = 0;
mutexVal.mutex = furi_mutex_alloc(FuriMutexTypeNormal);
if(!mutexVal.mutex) {
furi_message_queue_free(event_queue);
return 255;
}
ViewPort* view_port = view_port_alloc();
view_port_draw_callback_set(view_port, draw_callback, &mutexVal.mutex);
view_port_input_callback_set(view_port, input_callback, event_queue);
furi_hal_gpio_add_int_callback(&gpio_ext_pa7, gpiocallback, event_queue);
Gui* gui = furi_record_open(RECORD_GUI);
gui_add_view_port(gui, view_port, GuiLayerFullscreen);
FuriTimer* timer = furi_timer_alloc(clock_tick, FuriTimerTypePeriodic, event_queue);
furi_timer_start(timer, 1000);
FuriTimer* timerPause = furi_timer_alloc(clock_tick_pause, FuriTimerTypePeriodic, event_queue);
// ENABLE 5V pin
uint8_t attempts = 0;
while(!furi_hal_power_is_otg_enabled() && attempts++ < 5) {
furi_hal_power_enable_otg();
furi_delay_ms(10);
}
uint8_t diceBuffer[64];
for(uint8_t i = 0; i < 64; i++) diceBuffer[i] = 0;
uint8_t diceBufferCounter = 0;
uint8_t diceBufferPositionWrite = 0;
uint8_t diceBufferPositionRead = 0;
uint8_t tickCounter = 0;
uint32_t CRC32 = 0;
uint8_t method = 0;
// MD5
md5_context* md5_ctx = malloc(sizeof(md5_context));
uint8_t* hash = malloc(sizeof(uint8_t) * 16);
uint8_t* bufferTim2 = malloc(4);
md5_starts(md5_ctx);
uint8_t pause = 0;
while(1) {
FuriStatus event_status = furi_message_queue_get(event_queue, &event, FuriWaitForever);
uint8_t screenRefresh = 0;
if(event_status == FuriStatusOk) {
if(event.type == EventTypeInput) {
if(event.input.key == InputKeyBack && event.input.type == InputTypeLong) {
break;
} else if(pause == 0) {
if(event.input.key == InputKeyOk && event.input.type == InputTypeShort) {
if(diceBufferCounter > 0) {
furi_mutex_acquire(mutexVal.mutex, FuriWaitForever);
mutexVal.dice = diceBuffer[diceBufferPositionRead];
mutexVal.diceAvailiable = --diceBufferCounter;
mutexVal.pause = 1;
furi_mutex_release(mutexVal.mutex);
if(diceBufferPositionRead != 63)
diceBufferPositionRead++;
else
diceBufferPositionRead = 0;
pause = 1;
furi_timer_start(timerPause, 500);
screenRefresh = 1;
}
} else if(event.input.key == InputKeyLeft && event.input.type == InputTypeLong) {
if(method == 1) {
method = 0;
diceBufferPositionWrite = 0;
diceBufferPositionRead = 0;
diceBufferCounter = 0;
CRC32 = 0;
tickCounter = 0;
furi_mutex_acquire(mutexVal.mutex, FuriWaitForever);
mutexVal.method = 0;
mutexVal.dice = 0;
mutexVal.diceAvailiable = 0;
furi_mutex_release(mutexVal.mutex);
screenRefresh = 1;
}
} else if(event.input.key == InputKeyRight && event.input.type == InputTypeLong) {
if(method == 0) {
method = 1;
diceBufferPositionWrite = 0;
diceBufferPositionRead = 0;
diceBufferCounter = 0;
md5_starts(md5_ctx);
tickCounter = 0;
furi_mutex_acquire(mutexVal.mutex, FuriWaitForever);
mutexVal.method = 1;
mutexVal.dice = 0;
mutexVal.diceAvailiable = 0;
furi_mutex_release(mutexVal.mutex);
screenRefresh = 1;
}
}
}
} else if(event.type == ClockEventTypeTick) {
furi_mutex_acquire(mutexVal.mutex, FuriWaitForever);
mutexVal.cps = counter;
furi_mutex_release(mutexVal.mutex);
counter = 0;
screenRefresh = 1;
} else if(event.type == ClockEventTypeTickPause) {
furi_mutex_acquire(mutexVal.mutex, FuriWaitForever);
mutexVal.pause = 0;
furi_mutex_release(mutexVal.mutex);
furi_timer_stop(timerPause);
pause = 0;
screenRefresh = 1;
} else if(event.type == EventGPIO) {
if(diceBufferCounter < 64) {
// CRC32
if(method == 0) {
uint32_t TIM2Tick = TIM2->CNT;
bufferTim2[0] = (uint8_t)(TIM2Tick >> 24);
bufferTim2[1] = (uint8_t)(TIM2Tick >> 16);
bufferTim2[2] = (uint8_t)(TIM2Tick >> 8);
bufferTim2[3] = (uint8_t)TIM2Tick;
CRC32 = crc32_calc_buffer(CRC32, bufferTim2, 4);
tickCounter++;
if(tickCounter == 8) {
uint8_t localDice = CRC32 & 0b111;
if(localDice == 0 || localDice == 7) {
localDice = (diceBuffer[diceBufferPositionRead] >> 3) & 0b111;
}
if(localDice >= 1 && localDice <= 6) {
diceBuffer[diceBufferPositionWrite] = localDice;
diceBufferCounter++;
if(diceBufferPositionWrite != 63)
diceBufferPositionWrite++;
else
diceBufferPositionWrite = 0;
furi_mutex_acquire(mutexVal.mutex, FuriWaitForever);
mutexVal.diceAvailiable = diceBufferCounter;
furi_mutex_release(mutexVal.mutex);
screenRefresh = 1;
}
CRC32 = 0;
tickCounter = 0;
}
}
// MD5
else {
uint32_t tick = TIM2->CNT;
bufferTim2[0] = (uint8_t)(tick >> 24);
bufferTim2[1] = (uint8_t)(tick >> 16);
bufferTim2[2] = (uint8_t)(tick >> 8);
bufferTim2[3] = (uint8_t)tick;
md5_update(md5_ctx, bufferTim2, 4);
tickCounter++;
if(tickCounter == 32) {
md5_finish(md5_ctx, hash);
uint8_t localDice = 0;
for(uint8_t i = 0; i < 16; i++) {
localDice = hash[i] & 0b111;
if(localDice >= 1 && localDice <= 6) {
diceBuffer[diceBufferPositionWrite] = localDice;
diceBufferCounter++;
if(diceBufferPositionWrite != 63)
diceBufferPositionWrite++;
else
diceBufferPositionWrite = 0;
furi_mutex_acquire(mutexVal.mutex, FuriWaitForever);
mutexVal.diceAvailiable = diceBufferCounter;
furi_mutex_release(mutexVal.mutex);
screenRefresh = 1;
break;
}
}
md5_starts(md5_ctx);
tickCounter = 0;
}
}
}
counter++;
}
}
if(screenRefresh == 1) view_port_update(view_port);
}
LL_TIM_DisableCounter(TIM2);
furi_hal_bus_disable(FuriHalBusTIM2);
free(md5_ctx);
free(bufferTim2);
free(hash);
furi_record_close(RECORD_NOTIFICATION);
// Disable 5v power
if(furi_hal_power_is_otg_enabled()) {
furi_hal_power_disable_otg();
}
furi_hal_gpio_disable_int_callback(&gpio_ext_pa7);
furi_hal_gpio_remove_int_callback(&gpio_ext_pa7);
furi_message_queue_free(event_queue);
furi_mutex_free(mutexVal.mutex);
gui_remove_view_port(gui, view_port);
view_port_free(view_port);
furi_timer_free(timer);
furi_timer_free(timerPause);
furi_record_close(RECORD_GUI);
return 0;
}

View File

Before

Width:  |  Height:  |  Size: 3.5 KiB

After

Width:  |  Height:  |  Size: 3.5 KiB

View File

@@ -1,8 +1,7 @@
#pragma once #pragma once
#include "helpers/avr_isp_types.h" #include "helpers/avr_isp_types.h"
#include "avr_isp_icons.h" #include <avr_isp_icons.h>
#include <assets_icons.h>
#include "scenes/avr_isp_scene.h" #include "scenes/avr_isp_scene.h"
#include <gui/gui.h> #include <gui/gui.h>

View File

@@ -5,7 +5,7 @@
#define AVR_ISP_VERSION_APP "0.1" #define AVR_ISP_VERSION_APP "0.1"
#define AVR_ISP_DEVELOPED "SkorP" #define AVR_ISP_DEVELOPED "SkorP"
#define AVR_ISP_GITHUB "https://github.com/flipperdevices/flipperzero-firmware" #define AVR_ISP_GITHUB "https://github.com/flipperdevices/flipperzero-good-faps"
#define AVR_ISP_APP_FILE_VERSION 1 #define AVR_ISP_APP_FILE_VERSION 1
#define AVR_ISP_APP_FILE_TYPE "Flipper Dump AVR" #define AVR_ISP_APP_FILE_TYPE "Flipper Dump AVR"

View File

Before

Width:  |  Height:  |  Size: 3.5 KiB

After

Width:  |  Height:  |  Size: 3.5 KiB

View File

Before

Width:  |  Height:  |  Size: 4.4 KiB

After

Width:  |  Height:  |  Size: 4.4 KiB

View File

Before

Width:  |  Height:  |  Size: 3.7 KiB

After

Width:  |  Height:  |  Size: 3.7 KiB

View File

Before

Width:  |  Height:  |  Size: 3.6 KiB

After

Width:  |  Height:  |  Size: 3.6 KiB

View File

Before

Width:  |  Height:  |  Size: 3.6 KiB

After

Width:  |  Height:  |  Size: 3.6 KiB

View File

Before

Width:  |  Height:  |  Size: 3.7 KiB

After

Width:  |  Height:  |  Size: 3.7 KiB

View File

Before

Width:  |  Height:  |  Size: 2.4 KiB

After

Width:  |  Height:  |  Size: 2.4 KiB

View File

Before

Width:  |  Height:  |  Size: 3.9 KiB

After

Width:  |  Height:  |  Size: 3.9 KiB

View File

Before

Width:  |  Height:  |  Size: 3.8 KiB

After

Width:  |  Height:  |  Size: 3.8 KiB

View File

Before

Width:  |  Height:  |  Size: 3.6 KiB

After

Width:  |  Height:  |  Size: 3.6 KiB

View File

@@ -1,6 +1,8 @@
#include "../avr_isp_app_i.h" #include "../avr_isp_app_i.h"
#include <gui/modules/validators.h> #include <gui/modules/validators.h>
#define MAX_TEXT_INPUT_LEN 22
void avr_isp_scene_input_name_text_callback(void* context) { void avr_isp_scene_input_name_text_callback(void* context) {
furi_assert(context); furi_assert(context);
@@ -44,7 +46,7 @@ void avr_isp_scene_input_name_on_enter(void* context) {
avr_isp_scene_input_name_text_callback, avr_isp_scene_input_name_text_callback,
app, app,
app->file_name_tmp, app->file_name_tmp,
AVR_ISP_MAX_LEN_NAME, // buffer size MAX_TEXT_INPUT_LEN, // buffer size
dev_name_empty); dev_name_empty);
ValidatorIsFile* validator_is_file = ValidatorIsFile* validator_is_file =

View File

@@ -1,6 +1,5 @@
#include "avr_isp_view_chip_detect.h" #include "avr_isp_view_chip_detect.h"
#include "avr_isp_icons.h" #include <avr_isp_icons.h>
#include <assets_icons.h>
#include <gui/elements.h> #include <gui/elements.h>
#include "../helpers/avr_isp_worker_rw.h" #include "../helpers/avr_isp_worker_rw.h"

View File

@@ -1,6 +1,5 @@
#include "avr_isp_view_programmer.h" #include "avr_isp_view_programmer.h"
#include "avr_isp_icons.h" #include <avr_isp_icons.h>
#include <assets_icons.h>
#include "../helpers/avr_isp_worker.h" #include "../helpers/avr_isp_worker.h"
#include <gui/elements.h> #include <gui/elements.h>

View File

@@ -8,7 +8,7 @@ App(
fap_category="Tools", fap_category="Tools",
fap_icon="images/barcode_10.png", fap_icon="images/barcode_10.png",
fap_icon_assets="images", fap_icon_assets="images",
fap_file_assets="barcode_encoding_files", fap_file_assets="encoding_tables",
fap_author="@Kingal1337", fap_author="@Kingal1337",
fap_weburl="https://github.com/Kingal1337/flipper-barcode-generator", fap_weburl="https://github.com/Kingal1337/flipper-barcode-generator",
fap_version="1.1", fap_version="1.1",

View File

@@ -3,12 +3,13 @@ App(
name="BlackJack", name="BlackJack",
apptype=FlipperAppType.EXTERNAL, apptype=FlipperAppType.EXTERNAL,
entry_point="blackjack_app", entry_point="blackjack_app",
requires=["gui", "storage", "canvas"], requires=["gui","storage","canvas"],
stack_size=2 * 1024, stack_size=2 * 1024,
order=30,
fap_icon="blackjack_10px.png", fap_icon="blackjack_10px.png",
fap_category="Games", fap_category="Games",
fap_icon_assets="assets", fap_icon_assets="assets",
fap_author="@teeebor", fap_author="@teeebor",
fap_version="1.0", fap_version="1.1",
fap_description="Blackjack Game", fap_description="Blackjack Game",
) )

View File

@@ -15,7 +15,6 @@
#include "ui.h" #include "ui.h"
#include "blackjack_icons.h" #include "blackjack_icons.h"
#include <assets_icons.h>
#define DEALER_MAX 17 #define DEALER_MAX 17
@@ -34,9 +33,12 @@ static void draw_ui(Canvas* const canvas, const GameState* game_state) {
} }
static void render_callback(Canvas* const canvas, void* ctx) { static void render_callback(Canvas* const canvas, void* ctx) {
furi_assert(ctx);
const GameState* game_state = ctx; const GameState* game_state = ctx;
furi_mutex_acquire(game_state->mutex, FuriWaitForever); furi_mutex_acquire(game_state->mutex, 25);
if(game_state == NULL) {
return;
}
canvas_set_color(canvas, ColorBlack); canvas_set_color(canvas, ColorBlack);
canvas_draw_frame(canvas, 0, 0, 128, 64); canvas_draw_frame(canvas, 0, 0, 128, 64);
@@ -177,6 +179,7 @@ void lose(void* ctx) {
} }
void win(void* ctx) { void win(void* ctx) {
dolphin_deed(DolphinDeedPluginGameWin);
GameState* game_state = ctx; GameState* game_state = ctx;
game_state->state = GameStatePlay; game_state->state = GameStatePlay;
game_state->player_score += game_state->bet * 2; game_state->player_score += game_state->bet * 2;
@@ -277,7 +280,6 @@ void dealer_tick(GameState* game_state) {
if(dealer_score >= DEALER_MAX) { if(dealer_score >= DEALER_MAX) {
if(dealer_score > 21 || dealer_score < player_score) { if(dealer_score > 21 || dealer_score < player_score) {
dolphin_deed(DolphinDeedPluginGameWin);
enqueue( enqueue(
&(game_state->queue_state), &(game_state->queue_state),
game_state, game_state,
@@ -540,7 +542,7 @@ int32_t blackjack_app(void* p) {
int32_t return_code = 0; int32_t return_code = 0;
FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(AppEvent)); FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(AppEvent));
dolphin_deed(DolphinDeedPluginGameStart);
GameState* game_state = malloc(sizeof(GameState)); GameState* game_state = malloc(sizeof(GameState));
game_state->menu = malloc(sizeof(Menu)); game_state->menu = malloc(sizeof(Menu));
game_state->menu->menu_width = 40; game_state->menu->menu_width = 40;
@@ -571,9 +573,6 @@ int32_t blackjack_app(void* p) {
AppEvent event; AppEvent event;
// Call dolphin deed on game start
dolphin_deed(DolphinDeedPluginGameStart);
for(bool processing = true; processing;) { for(bool processing = true; processing;) {
FuriStatus event_status = furi_message_queue_get(event_queue, &event, 100); FuriStatus event_status = furi_message_queue_get(event_queue, &event, 100);
furi_mutex_acquire(game_state->mutex, FuriWaitForever); furi_mutex_acquire(game_state->mutex, FuriWaitForever);
@@ -612,8 +611,9 @@ int32_t blackjack_app(void* p) {
processing = game_state->processing; processing = game_state->processing;
} }
} }
view_port_update(view_port);
furi_mutex_release(game_state->mutex); furi_mutex_release(game_state->mutex);
view_port_update(view_port);
} }
furi_timer_free(timer); furi_timer_free(timer);

View File

@@ -54,7 +54,6 @@ typedef enum {
} Direction; } Direction;
typedef struct { typedef struct {
FuriMutex* mutex;
Card player_cards[21]; Card player_cards[21];
Card dealer_cards[21]; Card dealer_cards[21];
uint8_t player_card_count; uint8_t player_card_count;
@@ -74,4 +73,5 @@ typedef struct {
QueueState queue_state; QueueState queue_state;
Menu* menu; Menu* menu;
unsigned int last_tick; unsigned int last_tick;
FuriMutex* mutex;
} GameState; } GameState;

View File

@@ -81,7 +81,7 @@ void draw_score(Canvas* const canvas, bool top, uint8_t amount) {
void draw_money(Canvas* const canvas, uint32_t score) { void draw_money(Canvas* const canvas, uint32_t score) {
canvas_set_font(canvas, FontSecondary); canvas_set_font(canvas, FontSecondary);
char drawChar[11]; char drawChar[10];
uint32_t currAmount = score; uint32_t currAmount = score;
if(currAmount < 1000) { if(currAmount < 1000) {
snprintf(drawChar, sizeof(drawChar), "$%lu", currAmount); snprintf(drawChar, sizeof(drawChar), "$%lu", currAmount);

View File

@@ -7,10 +7,11 @@ App(
"gui", "gui",
], ],
stack_size=1 * 1024, stack_size=1 * 1024,
fap_icon="bomb.png", order=90,
fap_icon="bomb.png",
fap_category="Games", fap_category="Games",
fap_icon_assets="assets", fap_icon_assets="assets",
fap_author="@leo-need-more-coffee & @xMasterX", fap_author="@leo-need-more-coffee & @xMasterX",
fap_version="1.0", fap_version="1.1",
fap_description="Bomberduck(Bomberman) Game", fap_description="Bomberduck(Bomberman) Game",
) )

View File

@@ -6,7 +6,6 @@
#include <notification/notification.h> #include <notification/notification.h>
#include <notification/notification_messages.h> #include <notification/notification_messages.h>
#include "bomberduck_icons.h" #include "bomberduck_icons.h"
#include <assets_icons.h>
#include <dolphin/dolphin.h> #include <dolphin/dolphin.h>
int max(int a, int b) { int max(int a, int b) {
@@ -626,8 +625,8 @@ int32_t bomberduck_app(void* p) {
} }
} }
view_port_update(view_port);
furi_mutex_release(bomber_state->mutex); furi_mutex_release(bomber_state->mutex);
view_port_update(view_port);
} }
// Return to normal backlight settings // Return to normal backlight settings

View File

@@ -3,14 +3,14 @@ App(
name="BPM Tapper", name="BPM Tapper",
apptype=FlipperAppType.EXTERNAL, apptype=FlipperAppType.EXTERNAL,
entry_point="bpm_tapper_app", entry_point="bpm_tapper_app",
cdefines=["APP_BPM_TAPPER"],
requires=["gui"], requires=["gui"],
stack_size=2 * 1024, stack_size=2 * 1024,
fap_icon="bpm_10px.png", fap_icon="bpm_10px.png",
fap_category="Media", fap_category="Media",
fap_icon_assets="icons", fap_icon_assets="icons",
order=15,
fap_author="@panki27", fap_author="@panki27",
fap_weburl="https://github.com/panki27/bpm-tapper", fap_weburl="https://github.com/panki27/bpm-tapper",
fap_version="1.0", fap_version="1.1",
fap_description="Tap center button to measure BPM", fap_description="Tap center button to measure BPM",
) )

View File

@@ -3,8 +3,10 @@
#include <dialogs/dialogs.h> #include <dialogs/dialogs.h>
#include <gui/gui.h> #include <gui/gui.h>
#include <input/input.h> #include <input/input.h>
#include <core/string.h>
#include <stdlib.h> #include <stdlib.h>
#include "bpm_tapper_icons.h" #include "bpm_tapper_icons.h"
#include <assets_icons.h> #include <assets_icons.h>
typedef enum { typedef enum {
@@ -176,6 +178,7 @@ static void bpm_state_init(BPMTapper* const plugin_state) {
q = malloc(sizeof(queue)); q = malloc(sizeof(queue));
init_queue(q); init_queue(q);
plugin_state->tap_queue = q; plugin_state->tap_queue = q;
plugin_state->mutex = furi_mutex_alloc(FuriMutexTypeNormal);
} }
int32_t bpm_tapper_app(void* p) { int32_t bpm_tapper_app(void* p) {
@@ -187,7 +190,6 @@ int32_t bpm_tapper_app(void* p) {
// setup // setup
bpm_state_init(bpm_state); bpm_state_init(bpm_state);
bpm_state->mutex = furi_mutex_alloc(FuriMutexTypeNormal);
if(!bpm_state->mutex) { if(!bpm_state->mutex) {
FURI_LOG_E("BPM-Tapper", "cannot create mutex\r\n"); FURI_LOG_E("BPM-Tapper", "cannot create mutex\r\n");
free(bpm_state); free(bpm_state);
@@ -243,8 +245,9 @@ int32_t bpm_tapper_app(void* p) {
} }
} }
} }
view_port_update(view_port);
furi_mutex_release(bpm_state->mutex); furi_mutex_release(bpm_state->mutex);
view_port_update(view_port);
} }
view_port_enabled_set(view_port, false); view_port_enabled_set(view_port, false);
gui_remove_view_port(gui, view_port); gui_remove_view_port(gui, view_port);

View File

@@ -13,6 +13,6 @@ App(
fap_icon_assets="icons", fap_icon_assets="icons",
fap_author="@nymda", fap_author="@nymda",
fap_weburl="https://github.com/nymda/FlipperZeroBrainfuck", fap_weburl="https://github.com/nymda/FlipperZeroBrainfuck",
fap_version="1.0", fap_version="1.1",
fap_description="Brainfuck language interpreter", fap_description="Brainfuck language interpreter",
) )

View File

@@ -117,7 +117,6 @@ void brainfuck_free(BFApp* brainfuck) {
void brainfuck_show_loading_popup(void* context, bool show) { void brainfuck_show_loading_popup(void* context, bool show) {
BFApp* brainfuck = context; BFApp* brainfuck = context;
TaskHandle_t timer_task = xTaskGetHandle(configTIMER_SERVICE_TASK_NAME);
if(show) { if(show) {
// Raise timer priority so that animations can play // Raise timer priority so that animations can play

View File

@@ -29,8 +29,7 @@ typedef unsigned char byte;
#include <storage/storage.h> #include <storage/storage.h>
#include <lib/toolbox/path.h> #include <lib/toolbox/path.h>
#include "brainfuck_icons.h" #include <brainfuck_icons.h>
#include <assets_icons.h>
#include <storage/storage.h> #include <storage/storage.h>
#include <stream/stream.h> #include <stream/stream.h>
@@ -39,6 +38,8 @@ typedef unsigned char byte;
#include <notification/notification_messages.h> #include <notification/notification_messages.h>
#include <assets_icons.h>
#define BF_INST_BUFFER_SIZE 2048 #define BF_INST_BUFFER_SIZE 2048
#define BF_OUTPUT_SIZE 512 #define BF_OUTPUT_SIZE 512
#define BF_STACK_INITIAL_SIZE 128 #define BF_STACK_INITIAL_SIZE 128

View File

@@ -10,8 +10,9 @@ App(
stack_size=2 * 1024, stack_size=2 * 1024,
fap_icon="caesar_cipher_icon.png", fap_icon="caesar_cipher_icon.png",
fap_category="Tools", fap_category="Tools",
order=20,
fap_author="@panki27", fap_author="@panki27",
fap_weburl="https://github.com/panki27/caesar-cipher", fap_weburl="https://github.com/panki27/caesar-cipher",
fap_version="1.0", fap_version="1.1",
fap_description="Encrypt and decrypt text using Caesar Cipher", fap_description="Encrypt and decrypt text using Caesar Cipher",
) )

View File

@@ -7,8 +7,10 @@ App(
fap_category="GPIO", fap_category="GPIO",
fap_description="A camera suite application for the Flipper Zero ESP32-CAM module.", fap_description="A camera suite application for the Flipper Zero ESP32-CAM module.",
fap_icon="icons/camera_suite.png", fap_icon="icons/camera_suite.png",
fap_version="1.3",
fap_weburl="https://github.com/CodyTolene/Flipper-Zero-Cam", fap_weburl="https://github.com/CodyTolene/Flipper-Zero-Cam",
name="[ESP32] Camera Suite", name="[ESP32] Camera Suite",
order=1,
requires=["gui", "storage"], requires=["gui", "storage"],
stack_size=8 * 1024, stack_size=8 * 1024,
) )

View File

@@ -44,9 +44,12 @@ CameraSuite* camera_suite_app_alloc() {
// Set defaults, in case no config loaded // Set defaults, in case no config loaded
app->orientation = 0; // Orientation is "portrait", zero degrees by default. app->orientation = 0; // Orientation is "portrait", zero degrees by default.
app->haptic = 1; // Haptic is on by default app->dither = 0; // Dither algorithm is "Floyd Steinberg" by default.
app->speaker = 1; // Speaker is on by default app->flash = 1; // Flash is enabled by default.
app->led = 1; // LED is on by default app->haptic = 1; // Haptic is enabled by default
app->jpeg = 0; // Save JPEG to ESP32-CAM sd-card is disabled by default.
app->speaker = 1; // Speaker is enabled by default
app->led = 1; // LED is enabled by default
// Load configs // Load configs
camera_suite_read_settings(app); camera_suite_read_settings(app);

View File

@@ -30,7 +30,10 @@ typedef struct {
CameraSuiteViewCamera* camera_suite_view_camera; CameraSuiteViewCamera* camera_suite_view_camera;
CameraSuiteViewGuide* camera_suite_view_guide; CameraSuiteViewGuide* camera_suite_view_guide;
uint32_t orientation; uint32_t orientation;
uint32_t dither;
uint32_t flash;
uint32_t haptic; uint32_t haptic;
uint32_t jpeg;
uint32_t speaker; uint32_t speaker;
uint32_t led; uint32_t led;
ButtonMenu* button_menu; ButtonMenu* button_menu;
@@ -51,6 +54,22 @@ typedef enum {
CameraSuiteOrientation270, CameraSuiteOrientation270,
} CameraSuiteOrientationState; } CameraSuiteOrientationState;
typedef enum {
CameraSuiteDitherFloydSteinberg,
CameraSuiteDitherStucki,
CameraSuiteDitherJarvisJudiceNinke,
} CameraSuiteDitherState;
typedef enum {
CameraSuiteFlashOff,
CameraSuiteFlashOn,
} CameraSuiteFlashState;
typedef enum {
CameraSuiteJpegOff,
CameraSuiteJpegOn,
} CameraSuiteJpegState;
typedef enum { typedef enum {
CameraSuiteHapticOff, CameraSuiteHapticOff,
CameraSuiteHapticOn, CameraSuiteHapticOn,

View File

@@ -51,6 +51,9 @@ void camera_suite_save_settings(void* context) {
fff_file, BOILERPLATE_SETTINGS_HEADER, BOILERPLATE_SETTINGS_FILE_VERSION); fff_file, BOILERPLATE_SETTINGS_HEADER, BOILERPLATE_SETTINGS_FILE_VERSION);
flipper_format_write_uint32( flipper_format_write_uint32(
fff_file, BOILERPLATE_SETTINGS_KEY_ORIENTATION, &app->orientation, 1); fff_file, BOILERPLATE_SETTINGS_KEY_ORIENTATION, &app->orientation, 1);
flipper_format_write_uint32(fff_file, BOILERPLATE_SETTINGS_KEY_DITHER, &app->dither, 1);
flipper_format_write_uint32(fff_file, BOILERPLATE_SETTINGS_KEY_FLASH, &app->flash, 1);
flipper_format_write_uint32(fff_file, BOILERPLATE_SETTINGS_KEY_JPEG, &app->jpeg, 1);
flipper_format_write_uint32(fff_file, BOILERPLATE_SETTINGS_KEY_HAPTIC, &app->haptic, 1); flipper_format_write_uint32(fff_file, BOILERPLATE_SETTINGS_KEY_HAPTIC, &app->haptic, 1);
flipper_format_write_uint32(fff_file, BOILERPLATE_SETTINGS_KEY_SPEAKER, &app->speaker, 1); flipper_format_write_uint32(fff_file, BOILERPLATE_SETTINGS_KEY_SPEAKER, &app->speaker, 1);
flipper_format_write_uint32(fff_file, BOILERPLATE_SETTINGS_KEY_LED, &app->led, 1); flipper_format_write_uint32(fff_file, BOILERPLATE_SETTINGS_KEY_LED, &app->led, 1);
@@ -100,8 +103,12 @@ void camera_suite_read_settings(void* context) {
return; return;
} }
// Read settings
flipper_format_read_uint32( flipper_format_read_uint32(
fff_file, BOILERPLATE_SETTINGS_KEY_ORIENTATION, &app->orientation, 1); fff_file, BOILERPLATE_SETTINGS_KEY_ORIENTATION, &app->orientation, 1);
flipper_format_read_uint32(fff_file, BOILERPLATE_SETTINGS_KEY_DITHER, &app->dither, 1);
flipper_format_read_uint32(fff_file, BOILERPLATE_SETTINGS_KEY_FLASH, &app->flash, 1);
flipper_format_read_uint32(fff_file, BOILERPLATE_SETTINGS_KEY_JPEG, &app->jpeg, 1);
flipper_format_read_uint32(fff_file, BOILERPLATE_SETTINGS_KEY_HAPTIC, &app->haptic, 1); flipper_format_read_uint32(fff_file, BOILERPLATE_SETTINGS_KEY_HAPTIC, &app->haptic, 1);
flipper_format_read_uint32(fff_file, BOILERPLATE_SETTINGS_KEY_SPEAKER, &app->speaker, 1); flipper_format_read_uint32(fff_file, BOILERPLATE_SETTINGS_KEY_SPEAKER, &app->speaker, 1);
flipper_format_read_uint32(fff_file, BOILERPLATE_SETTINGS_KEY_LED, &app->led, 1); flipper_format_read_uint32(fff_file, BOILERPLATE_SETTINGS_KEY_LED, &app->led, 1);

View File

@@ -2,14 +2,21 @@
#include <string.h> #include <string.h>
#include <storage/storage.h> #include <storage/storage.h>
#include <flipper_format/flipper_format_i.h> #include <flipper_format/flipper_format_i.h>
#include "../camera_suite.h" #include "../camera_suite.h"
#ifndef CAMERA_SUITE_STORAGE_H
#define CAMERA_SUITE_STORAGE_H
#define BOILERPLATE_SETTINGS_FILE_VERSION 1 #define BOILERPLATE_SETTINGS_FILE_VERSION 1
#define CONFIG_FILE_DIRECTORY_PATH EXT_PATH("apps_data/camera_suite") #define CONFIG_FILE_DIRECTORY_PATH EXT_PATH("apps_data/camera_suite")
#define BOILERPLATE_SETTINGS_SAVE_PATH CONFIG_FILE_DIRECTORY_PATH "/camera_suite.conf" #define BOILERPLATE_SETTINGS_SAVE_PATH CONFIG_FILE_DIRECTORY_PATH "/camera_suite.conf"
#define BOILERPLATE_SETTINGS_SAVE_PATH_TMP BOILERPLATE_SETTINGS_SAVE_PATH ".tmp" #define BOILERPLATE_SETTINGS_SAVE_PATH_TMP BOILERPLATE_SETTINGS_SAVE_PATH ".tmp"
#define BOILERPLATE_SETTINGS_HEADER "Camera Suite Config File" #define BOILERPLATE_SETTINGS_HEADER "Camera Suite Config File"
#define BOILERPLATE_SETTINGS_KEY_ORIENTATION "Orientation" #define BOILERPLATE_SETTINGS_KEY_ORIENTATION "Orientation"
#define BOILERPLATE_SETTINGS_KEY_DITHER "Dither"
#define BOILERPLATE_SETTINGS_KEY_FLASH "Flash"
#define BOILERPLATE_SETTINGS_KEY_JPEG "SaveJPEG"
#define BOILERPLATE_SETTINGS_KEY_HAPTIC "Haptic" #define BOILERPLATE_SETTINGS_KEY_HAPTIC "Haptic"
#define BOILERPLATE_SETTINGS_KEY_LED "Led" #define BOILERPLATE_SETTINGS_KEY_LED "Led"
#define BOILERPLATE_SETTINGS_KEY_SPEAKER "Speaker" #define BOILERPLATE_SETTINGS_KEY_SPEAKER "Speaker"
@@ -18,3 +25,5 @@
void camera_suite_save_settings(void* context); void camera_suite_save_settings(void* context);
void camera_suite_read_settings(void* context); void camera_suite_read_settings(void* context);
#endif

View File

@@ -16,6 +16,39 @@ const uint32_t orientation_value[4] = {
CameraSuiteOrientation270, CameraSuiteOrientation270,
}; };
// Possible dithering types for the camera.
const char* const dither_text[28] = {
"Floyd-Steinberg",
"Stucki",
"Jarvis-Judice-Ninke",
};
const uint32_t dither_value[4] = {
CameraSuiteDitherFloydSteinberg,
CameraSuiteDitherStucki,
CameraSuiteDitherJarvisJudiceNinke,
};
const char* const flash_text[2] = {
"OFF",
"ON",
};
const uint32_t flash_value[2] = {
CameraSuiteFlashOff,
CameraSuiteFlashOn,
};
const char* const jpeg_text[2] = {
"OFF",
"ON",
};
const uint32_t jpeg_value[2] = {
CameraSuiteJpegOff,
CameraSuiteJpegOn,
};
const char* const haptic_text[2] = { const char* const haptic_text[2] = {
"OFF", "OFF",
"ON", "ON",
@@ -54,6 +87,30 @@ static void camera_suite_scene_settings_set_camera_orientation(VariableItem* ite
app->orientation = orientation_value[index]; app->orientation = orientation_value[index];
} }
static void camera_suite_scene_settings_set_camera_dither(VariableItem* item) {
CameraSuite* app = variable_item_get_context(item);
uint8_t index = variable_item_get_current_value_index(item);
variable_item_set_current_value_text(item, dither_text[index]);
app->dither = dither_value[index];
}
static void camera_suite_scene_settings_set_flash(VariableItem* item) {
CameraSuite* app = variable_item_get_context(item);
uint8_t index = variable_item_get_current_value_index(item);
variable_item_set_current_value_text(item, flash_text[index]);
app->flash = flash_value[index];
}
static void camera_suite_scene_settings_set_jpeg(VariableItem* item) {
CameraSuite* app = variable_item_get_context(item);
uint8_t index = variable_item_get_current_value_index(item);
variable_item_set_current_value_text(item, jpeg_text[index]);
app->jpeg = jpeg_value[index];
}
static void camera_suite_scene_settings_set_haptic(VariableItem* item) { static void camera_suite_scene_settings_set_haptic(VariableItem* item) {
CameraSuite* app = variable_item_get_context(item); CameraSuite* app = variable_item_get_context(item);
uint8_t index = variable_item_get_current_value_index(item); uint8_t index = variable_item_get_current_value_index(item);
@@ -97,6 +154,38 @@ void camera_suite_scene_settings_on_enter(void* context) {
variable_item_set_current_value_index(item, value_index); variable_item_set_current_value_index(item, value_index);
variable_item_set_current_value_text(item, orientation_text[value_index]); variable_item_set_current_value_text(item, orientation_text[value_index]);
// Camera Dither Type
item = variable_item_list_add(
app->variable_item_list,
"Dithering Type:",
3,
camera_suite_scene_settings_set_camera_dither,
app);
value_index = value_index_uint32(app->dither, dither_value, 3);
variable_item_set_current_value_index(item, value_index);
variable_item_set_current_value_text(item, dither_text[value_index]);
// Flash ON/OFF
item = variable_item_list_add(
app->variable_item_list, "Flash:", 2, camera_suite_scene_settings_set_flash, app);
value_index = value_index_uint32(app->flash, flash_value, 2);
variable_item_set_current_value_index(item, value_index);
variable_item_set_current_value_text(item, flash_text[value_index]);
// @todo - Save picture to ESP32-CAM sd-card instead of Flipper Zero
// sd-card. This hides the setting for it, for now.
// Save JPEG to ESP32-CAM sd-card instead of Flipper Zero sd-card ON/OFF
// item = variable_item_list_add(
// app->variable_item_list,
// "Save JPEG to ext sdcard:",
// 2,
// camera_suite_scene_settings_set_jpeg,
// app);
// value_index = value_index_uint32(app->jpeg, jpeg_value, 2);
// variable_item_set_current_value_index(item, value_index);
// variable_item_set_current_value_text(item, jpeg_text[value_index]);
UNUSED(camera_suite_scene_settings_set_jpeg);
// Haptic FX ON/OFF // Haptic FX ON/OFF
item = variable_item_list_add( item = variable_item_list_add(
app->variable_item_list, "Haptic FX:", 2, camera_suite_scene_settings_set_haptic, app); app->variable_item_list, "Haptic FX:", 2, camera_suite_scene_settings_set_haptic, app);

View File

@@ -8,48 +8,39 @@
#include "../helpers/camera_suite_speaker.h" #include "../helpers/camera_suite_speaker.h"
#include "../helpers/camera_suite_led.h" #include "../helpers/camera_suite_led.h"
static CameraSuiteViewCamera* current_instance = NULL;
struct CameraSuiteViewCamera {
CameraSuiteViewCameraCallback callback;
FuriStreamBuffer* rx_stream;
FuriThread* worker_thread;
View* view;
void* context;
};
void camera_suite_view_camera_set_callback(
CameraSuiteViewCamera* instance,
CameraSuiteViewCameraCallback callback,
void* context) {
furi_assert(instance);
furi_assert(callback);
instance->callback = callback;
instance->context = context;
}
// Function to draw pixels on the canvas based on camera orientation
static void draw_pixel_by_orientation(Canvas* canvas, uint8_t x, uint8_t y, uint8_t orientation) { static void draw_pixel_by_orientation(Canvas* canvas, uint8_t x, uint8_t y, uint8_t orientation) {
furi_assert(canvas);
furi_assert(x);
furi_assert(y);
furi_assert(orientation);
switch(orientation) { switch(orientation) {
case 0: // Camera rotated 0 degrees (right side up, default) default:
case 0: { // Camera rotated 0 degrees (right side up, default)
canvas_draw_dot(canvas, x, y); canvas_draw_dot(canvas, x, y);
break; break;
case 1: // Camera rotated 90 degrees }
case 1: { // Camera rotated 90 degrees
canvas_draw_dot(canvas, y, FRAME_WIDTH - 1 - x); canvas_draw_dot(canvas, y, FRAME_WIDTH - 1 - x);
break; break;
case 2: // Camera rotated 180 degrees (upside down) }
case 2: { // Camera rotated 180 degrees (upside down)
canvas_draw_dot(canvas, FRAME_WIDTH - 1 - x, FRAME_HEIGHT - 1 - y); canvas_draw_dot(canvas, FRAME_WIDTH - 1 - x, FRAME_HEIGHT - 1 - y);
break; break;
case 3: // Camera rotated 270 degrees }
case 3: { // Camera rotated 270 degrees
canvas_draw_dot(canvas, FRAME_HEIGHT - 1 - y, x); canvas_draw_dot(canvas, FRAME_HEIGHT - 1 - y, x);
break; break;
default: }
break;
} }
} }
static void camera_suite_view_camera_draw(Canvas* canvas, void* _model) { static void camera_suite_view_camera_draw(Canvas* canvas, void* model) {
UartDumpModel* model = _model; furi_assert(canvas);
furi_assert(model);
UartDumpModel* uartDumpModel = model;
// Clear the screen. // Clear the screen.
canvas_set_color(canvas, ColorBlack); canvas_set_color(canvas, ColorBlack);
@@ -57,21 +48,19 @@ static void camera_suite_view_camera_draw(Canvas* canvas, void* _model) {
// Draw the frame. // Draw the frame.
canvas_draw_frame(canvas, 0, 0, FRAME_WIDTH, FRAME_HEIGHT); canvas_draw_frame(canvas, 0, 0, FRAME_WIDTH, FRAME_HEIGHT);
CameraSuite* app = current_instance->context;
for(size_t p = 0; p < FRAME_BUFFER_LENGTH; ++p) { for(size_t p = 0; p < FRAME_BUFFER_LENGTH; ++p) {
uint8_t x = p % ROW_BUFFER_LENGTH; // 0 .. 15 uint8_t x = p % ROW_BUFFER_LENGTH; // 0 .. 15
uint8_t y = p / ROW_BUFFER_LENGTH; // 0 .. 63 uint8_t y = p / ROW_BUFFER_LENGTH; // 0 .. 63
for(uint8_t i = 0; i < 8; ++i) { for(uint8_t i = 0; i < 8; ++i) {
if((model->pixels[p] & (1 << (7 - i))) != 0) { if((uartDumpModel->pixels[p] & (1 << (7 - i))) != 0) {
draw_pixel_by_orientation(canvas, (x * 8) + i, y, app->orientation); draw_pixel_by_orientation(canvas, (x * 8) + i, y, uartDumpModel->orientation);
} }
} }
} }
// Draw the guide if the camera is not initialized. // Draw the guide if the camera is not initialized.
if(!model->initialized) { if(!uartDumpModel->initialized) {
canvas_draw_icon(canvas, 74, 16, &I_DolphinCommon_56x48); canvas_draw_icon(canvas, 74, 16, &I_DolphinCommon_56x48);
canvas_set_font(canvas, FontSecondary); canvas_set_font(canvas, FontSecondary);
canvas_draw_str(canvas, 8, 12, "Connect the ESP32-CAM"); canvas_draw_str(canvas, 8, 12, "Connect the ESP32-CAM");
@@ -82,15 +71,106 @@ static void camera_suite_view_camera_draw(Canvas* canvas, void* _model) {
} }
} }
static void camera_suite_view_camera_model_init(UartDumpModel* const model) { static void save_image(void* model) {
furi_assert(model);
UartDumpModel* uartDumpModel = model;
// This pointer is used to access the storage.
Storage* storage = furi_record_open(RECORD_STORAGE);
// This pointer is used to access the filesystem.
File* file = storage_file_alloc(storage);
// Store path in local variable.
const char* folderName = EXT_PATH("DCIM");
// Create the folder name for the image file if it does not exist.
if(storage_common_stat(storage, folderName, NULL) == FSE_NOT_EXIST) {
storage_simply_mkdir(storage, folderName);
}
// This pointer is used to access the file name.
FuriString* file_name = furi_string_alloc();
// Get the current date and time.
FuriHalRtcDateTime datetime = {0};
furi_hal_rtc_get_datetime(&datetime);
// Create the file name.
furi_string_printf(
file_name,
EXT_PATH("DCIM/%.4d%.2d%.2d-%.2d%.2d%.2d.bmp"),
datetime.year,
datetime.month,
datetime.day,
datetime.hour,
datetime.minute,
datetime.second);
// Open the file for writing. If the file does not exist (it shouldn't),
// create it.
bool result =
storage_file_open(file, furi_string_get_cstr(file_name), FSAM_WRITE, FSOM_OPEN_ALWAYS);
// Free the file name after use.
furi_string_free(file_name);
if(!uartDumpModel->inverted) {
for(size_t i = 0; i < FRAME_BUFFER_LENGTH; ++i) {
uartDumpModel->pixels[i] = ~uartDumpModel->pixels[i];
}
}
// If the file was opened successfully, write the bitmap header and the
// image data.
if(result) {
// Write BMP Header
storage_file_write(file, bitmap_header, BITMAP_HEADER_LENGTH);
// @todo - Add a function for saving the image directly from the
// ESP32-CAM to the Flipper Zero SD card.
// Write locally to the Flipper Zero SD card in the DCIM folder.
int8_t row_buffer[ROW_BUFFER_LENGTH];
// @todo - Save image based on orientation.
for(size_t i = 64; i > 0; --i) {
for(size_t j = 0; j < ROW_BUFFER_LENGTH; ++j) {
row_buffer[j] = uartDumpModel->pixels[((i - 1) * ROW_BUFFER_LENGTH) + j];
}
storage_file_write(file, row_buffer, ROW_BUFFER_LENGTH);
}
}
// Close the file.
storage_file_close(file);
// Free up memory.
storage_file_free(file);
}
static void
camera_suite_view_camera_model_init(UartDumpModel* const model, CameraSuite* instance_context) {
furi_assert(model);
furi_assert(instance_context);
for(size_t i = 0; i < FRAME_BUFFER_LENGTH; i++) { for(size_t i = 0; i < FRAME_BUFFER_LENGTH; i++) {
model->pixels[i] = 0; model->pixels[i] = 0;
} }
uint32_t orientation = instance_context->orientation;
model->flash = instance_context->flash;
model->inverted = false;
model->orientation = orientation;
} }
static bool camera_suite_view_camera_input(InputEvent* event, void* context) { static bool camera_suite_view_camera_input(InputEvent* event, void* context) {
furi_assert(context); furi_assert(context);
furi_assert(event);
CameraSuiteViewCamera* instance = context; CameraSuiteViewCamera* instance = context;
if(event->type == InputTypeRelease) { if(event->type == InputTypeRelease) {
switch(event->key) { switch(event->key) {
default: // Stop all sounds, reset the LED. default: // Stop all sounds, reset the LED.
@@ -106,13 +186,18 @@ static bool camera_suite_view_camera_input(InputEvent* event, void* context) {
true); true);
break; break;
} }
// Send `data` to the ESP32-CAM
} else if(event->type == InputTypePress) { } else if(event->type == InputTypePress) {
uint8_t data[1]; uint8_t data[1] = {'X'};
switch(event->key) { switch(event->key) {
case InputKeyBack: // Camera: Stop stream.
// Stop the camera stream. case InputKeyBack: {
data[0] = 's'; // Set the camera flash to off.
uint8_t flash_off = 'f';
furi_hal_uart_tx(UART_CH, &flash_off, 1);
furi_delay_ms(50);
// Stop camera stream.
uint8_t stop_camera = 's';
furi_hal_uart_tx(UART_CH, &stop_camera, 1);
// Go back to the main menu. // Go back to the main menu.
with_view_model( with_view_model(
instance->view, instance->view,
@@ -123,9 +208,9 @@ static bool camera_suite_view_camera_input(InputEvent* event, void* context) {
}, },
true); true);
break; break;
case InputKeyLeft: }
// Camera: Invert. // Camera: Toggle invert on the ESP32-CAM.
data[0] = '<'; case InputKeyLeft: {
with_view_model( with_view_model(
instance->view, instance->view,
UartDumpModel * model, UartDumpModel * model,
@@ -134,12 +219,20 @@ static bool camera_suite_view_camera_input(InputEvent* event, void* context) {
camera_suite_play_happy_bump(instance->context); camera_suite_play_happy_bump(instance->context);
camera_suite_play_input_sound(instance->context); camera_suite_play_input_sound(instance->context);
camera_suite_led_set_rgb(instance->context, 0, 0, 255); camera_suite_led_set_rgb(instance->context, 0, 0, 255);
if(model->inverted) {
data[0] = 'i';
model->inverted = false;
} else {
data[0] = 'I';
model->inverted = true;
}
instance->callback(CameraSuiteCustomEventSceneCameraLeft, instance->context); instance->callback(CameraSuiteCustomEventSceneCameraLeft, instance->context);
}, },
true); true);
break; break;
case InputKeyRight: }
// Camera: Enable/disable dithering. // Camera: Enable/disable dithering.
case InputKeyRight: {
data[0] = '>'; data[0] = '>';
with_view_model( with_view_model(
instance->view, instance->view,
@@ -153,8 +246,9 @@ static bool camera_suite_view_camera_input(InputEvent* event, void* context) {
}, },
true); true);
break; break;
case InputKeyUp: }
// Camera: Increase contrast. // Camera: Increase contrast.
case InputKeyUp: {
data[0] = 'C'; data[0] = 'C';
with_view_model( with_view_model(
instance->view, instance->view,
@@ -168,8 +262,9 @@ static bool camera_suite_view_camera_input(InputEvent* event, void* context) {
}, },
true); true);
break; break;
case InputKeyDown: }
// Camera: Reduce contrast. // Camera: Reduce contrast.
case InputKeyDown: {
data[0] = 'c'; data[0] = 'c';
with_view_model( with_view_model(
instance->view, instance->view,
@@ -183,58 +278,113 @@ static bool camera_suite_view_camera_input(InputEvent* event, void* context) {
}, },
true); true);
break; break;
case InputKeyOk: }
// Switch dithering types. // Camera: Take picture.
data[0] = 'D'; case InputKeyOk: {
with_view_model( with_view_model(
instance->view, instance->view,
UartDumpModel * model, UartDumpModel * model,
{ {
UNUSED(model); camera_suite_play_long_bump(instance->context);
camera_suite_play_happy_bump(instance->context);
camera_suite_play_input_sound(instance->context); camera_suite_play_input_sound(instance->context);
camera_suite_led_set_rgb(instance->context, 0, 0, 255); camera_suite_led_set_rgb(instance->context, 0, 0, 255);
// Save picture directly to ESP32-CAM.
// @todo - Add this functionality.
// data[0] = 'P';
// furi_hal_uart_tx(UART_CH, data, 1);
// if(model->flash) {
// data[0] = 'F';
// furi_hal_uart_tx(UART_CH, data, 1);
// furi_delay_ms(50);
// }
// Take a picture.
save_image(model);
// if(model->flash) {
// data[0] = 'f';
// }
instance->callback(CameraSuiteCustomEventSceneCameraOk, instance->context); instance->callback(CameraSuiteCustomEventSceneCameraOk, instance->context);
}, },
true); true);
break; break;
}
// Camera: Do nothing.
case InputKeyMAX: case InputKeyMAX:
default: {
break; break;
} }
// Send `data` to the ESP32-CAM }
furi_hal_uart_tx(UART_CH, data, 1);
if(data[0] != 'X') {
// Send `data` to the ESP32-CAM.
furi_hal_uart_tx(UART_CH, data, 1);
}
} }
return true; return true;
} }
static void camera_suite_view_camera_exit(void* context) { static void camera_suite_view_camera_exit(void* context) {
furi_assert(context); UNUSED(context);
// Set the camera flash to off.
uint8_t flash_off = 'f';
furi_hal_uart_tx(UART_CH, &flash_off, 1);
furi_delay_ms(50);
// Stop camera stream.
uint8_t stop_camera = 's';
furi_hal_uart_tx(UART_CH, &stop_camera, 1);
furi_delay_ms(50);
} }
static void camera_suite_view_camera_enter(void* context) { static void camera_suite_view_camera_enter(void* context) {
// Check `context` for null. If it is null, abort program, else continue.
furi_assert(context); furi_assert(context);
// Cast `context` to `CameraSuiteViewCamera*` and store it in `instance`. // Get the camera suite instance context.
CameraSuiteViewCamera* instance = (CameraSuiteViewCamera*)context; CameraSuiteViewCamera* instance = (CameraSuiteViewCamera*)context;
// Assign the current instance to the global variable // Get the camera suite instance context.
current_instance = instance; CameraSuite* instance_context = instance->context;
uint8_t data[1]; // Start camera stream.
data[0] = 'S'; // Uppercase `S` to start the camera uint8_t start_camera = 'S';
// Send `data` to the ESP32-CAM furi_hal_uart_tx(UART_CH, &start_camera, 1);
furi_hal_uart_tx(UART_CH, data, 1); furi_delay_ms(75);
// Get/set dither type.
uint8_t dither_type = instance_context->dither;
furi_hal_uart_tx(UART_CH, &dither_type, 1);
furi_delay_ms(75);
// Make sure the camera is not inverted.
uint8_t invert_camera = 'i';
furi_hal_uart_tx(UART_CH, &invert_camera, 1);
furi_delay_ms(75);
// Toggle flash on or off based on the current state. This will keep the
// flash on initially. However we're toggling it for now on input.
uint8_t flash_state = instance_context->flash ? 'F' : 'f';
furi_hal_uart_tx(UART_CH, &flash_state, 1);
furi_delay_ms(75);
// Make sure we start with the flash off.
// uint8_t flash_state = 'f';
// furi_hal_uart_tx(UART_CH, &flash_state, 1);
// furi_delay_ms(75);
with_view_model( with_view_model(
instance->view, instance->view,
UartDumpModel * model, UartDumpModel * model,
{ camera_suite_view_camera_model_init(model); }, { camera_suite_view_camera_model_init(model, instance_context); },
true); true);
} }
static void camera_on_irq_cb(UartIrqEvent uartIrqEvent, uint8_t data, void* context) { static void camera_on_irq_cb(UartIrqEvent uartIrqEvent, uint8_t data, void* context) {
// Check `context` for null. If it is null, abort program, else continue. furi_assert(uartIrqEvent);
furi_assert(data);
furi_assert(context); furi_assert(context);
// Cast `context` to `CameraSuiteViewCamera*` and store it in `instance`. // Cast `context` to `CameraSuiteViewCamera*` and store it in `instance`.
@@ -248,47 +398,57 @@ static void camera_on_irq_cb(UartIrqEvent uartIrqEvent, uint8_t data, void* cont
} }
} }
static void process_ringbuffer(UartDumpModel* model, uint8_t byte) { static void process_ringbuffer(UartDumpModel* model, uint8_t const byte) {
// First char has to be 'Y' in the buffer. furi_assert(model);
if(model->ringbuffer_index == 0 && byte != 'Y') { furi_assert(byte);
// The first HEADER_LENGTH bytes are reserved for header information.
if(model->ringbuffer_index < HEADER_LENGTH) {
// Validate the start of row characters 'Y' and ':'.
if(model->ringbuffer_index == 0 && byte != 'Y') {
// Incorrect start of frame; reset.
return;
}
if(model->ringbuffer_index == 1 && byte != ':') {
// Incorrect start of frame; reset.
model->ringbuffer_index = 0;
return;
}
if(model->ringbuffer_index == 2) {
// Assign the third byte as the row identifier.
model->row_identifier = byte;
}
model->ringbuffer_index++; // Increment index for the next byte.
return; return;
} }
// Second char has to be ':' in the buffer or reset. // Store pixel value directly after the header.
if(model->ringbuffer_index == 1 && byte != ':') { model->row_ringbuffer[model->ringbuffer_index - HEADER_LENGTH] = byte;
model->ringbuffer_index = 0; model->ringbuffer_index++; // Increment index for the next byte.
process_ringbuffer(model, byte);
return;
}
// Assign current byte to the ringbuffer. // Check whether the ring buffer is filled.
model->row_ringbuffer[model->ringbuffer_index] = byte; if(model->ringbuffer_index >= RING_BUFFER_LENGTH) {
// Increment the ringbuffer index. model->ringbuffer_index = 0; // Reset the ring buffer index.
++model->ringbuffer_index; model->initialized = true; // Set the connection as successfully established.
// Let's wait 'till the buffer fills. // Compute the starting index for the row in the pixel buffer.
if(model->ringbuffer_index < RING_BUFFER_LENGTH) { size_t row_start_index = model->row_identifier * ROW_BUFFER_LENGTH;
return;
}
// Flush the ringbuffer to the framebuffer. // Ensure the row start index is within the valid range.
model->ringbuffer_index = 0; // Reset the ringbuffer if(row_start_index > LAST_ROW_INDEX) {
model->initialized = true; // Established the connection successfully. row_start_index = 0; // Reset to a safe value in case of an overflow.
size_t row_start_index = }
model->row_ringbuffer[2] * ROW_BUFFER_LENGTH; // Third char will determine the row number
if(row_start_index > LAST_ROW_INDEX) { // Failsafe // Flush the contents of the ring buffer to the pixel buffer.
row_start_index = 0; for(size_t i = 0; i < ROW_BUFFER_LENGTH; ++i) {
} model->pixels[row_start_index + i] = model->row_ringbuffer[i];
}
for(size_t i = 0; i < ROW_BUFFER_LENGTH; ++i) {
model->pixels[row_start_index + i] =
model->row_ringbuffer[i + 3]; // Writing the remaining 16 bytes into the frame buffer
} }
} }
static int32_t camera_worker(void* context) { static int32_t camera_worker(void* context) {
furi_assert(context); furi_assert(context);
CameraSuiteViewCamera* instance = context; CameraSuiteViewCamera* instance = context;
while(1) { while(1) {
@@ -328,36 +488,49 @@ static int32_t camera_worker(void* context) {
} }
CameraSuiteViewCamera* camera_suite_view_camera_alloc() { CameraSuiteViewCamera* camera_suite_view_camera_alloc() {
// Allocate memory for the instance
CameraSuiteViewCamera* instance = malloc(sizeof(CameraSuiteViewCamera)); CameraSuiteViewCamera* instance = malloc(sizeof(CameraSuiteViewCamera));
// Allocate the view object
instance->view = view_alloc(); instance->view = view_alloc();
// Allocate a stream buffer
instance->rx_stream = furi_stream_buffer_alloc(2048, 1); instance->rx_stream = furi_stream_buffer_alloc(2048, 1);
// Set up views // Allocate model
view_allocate_model(instance->view, ViewModelTypeLocking, sizeof(UartDumpModel)); view_allocate_model(instance->view, ViewModelTypeLocking, sizeof(UartDumpModel));
view_set_context(instance->view, instance); // furi_assert crashes in events without this
// Set context for the view
view_set_context(instance->view, instance);
// Set draw callback
view_set_draw_callback(instance->view, (ViewDrawCallback)camera_suite_view_camera_draw); view_set_draw_callback(instance->view, (ViewDrawCallback)camera_suite_view_camera_draw);
// Set input callback
view_set_input_callback(instance->view, camera_suite_view_camera_input); view_set_input_callback(instance->view, camera_suite_view_camera_input);
// Set enter callback
view_set_enter_callback(instance->view, camera_suite_view_camera_enter); view_set_enter_callback(instance->view, camera_suite_view_camera_enter);
// Set exit callback
view_set_exit_callback(instance->view, camera_suite_view_camera_exit); view_set_exit_callback(instance->view, camera_suite_view_camera_exit);
with_view_model( // Allocate a thread for this camera to run on.
instance->view, FuriThread* thread = furi_thread_alloc_ex("UsbUartWorker", 2048, camera_worker, instance);
UartDumpModel * model, instance->worker_thread = thread;
{ camera_suite_view_camera_model_init(model); },
true);
instance->worker_thread = furi_thread_alloc_ex("UsbUartWorker", 2048, camera_worker, instance);
furi_thread_start(instance->worker_thread); furi_thread_start(instance->worker_thread);
// Enable uart listener // Enable uart listener
if(UART_CH == FuriHalUartIdUSART1) { if(UART_CH == UART_CH) {
furi_hal_console_disable(); furi_hal_console_disable();
} else if(UART_CH == FuriHalUartIdLPUART1) { } else if(UART_CH == FuriHalUartIdLPUART1) {
furi_hal_uart_init(UART_CH, 230400); furi_hal_uart_init(UART_CH, 230400);
} }
// 115200 is the default baud rate for the ESP32-CAM.
furi_hal_uart_set_br(UART_CH, 230400); furi_hal_uart_set_br(UART_CH, 230400);
// Enable UART1 and set the IRQ callback.
furi_hal_uart_set_irq_cb(UART_CH, camera_on_irq_cb, instance); furi_hal_uart_set_irq_cb(UART_CH, camera_on_irq_cb, instance);
return instance; return instance;
@@ -366,21 +539,39 @@ CameraSuiteViewCamera* camera_suite_view_camera_alloc() {
void camera_suite_view_camera_free(CameraSuiteViewCamera* instance) { void camera_suite_view_camera_free(CameraSuiteViewCamera* instance) {
furi_assert(instance); furi_assert(instance);
with_view_model( // Remove the IRQ callback.
instance->view, UartDumpModel * model, { UNUSED(model); }, true);
view_free(instance->view);
free(instance);
furi_hal_uart_set_irq_cb(UART_CH, NULL, NULL); furi_hal_uart_set_irq_cb(UART_CH, NULL, NULL);
// Free the worker thread.
furi_thread_free(instance->worker_thread);
// Free the allocated stream buffer.
furi_stream_buffer_free(instance->rx_stream);
// Re-enable the console.
if(UART_CH == FuriHalUartIdLPUART1) { if(UART_CH == FuriHalUartIdLPUART1) {
furi_hal_uart_deinit(UART_CH); furi_hal_uart_deinit(UART_CH);
} else { } else {
furi_hal_console_enable(); furi_hal_console_enable();
} }
with_view_model(
instance->view, UartDumpModel * model, { UNUSED(model); }, true);
view_free(instance->view);
free(instance);
} }
View* camera_suite_view_camera_get_view(CameraSuiteViewCamera* instance) { View* camera_suite_view_camera_get_view(CameraSuiteViewCamera* instance) {
furi_assert(instance); furi_assert(instance);
return instance->view; return instance->view;
} }
void camera_suite_view_camera_set_callback(
CameraSuiteViewCamera* instance,
CameraSuiteViewCameraCallback callback,
void* context) {
furi_assert(instance);
furi_assert(callback);
instance->callback = callback;
instance->context = context;
}

View File

@@ -1,3 +1,5 @@
#pragma once
#include "../helpers/camera_suite_custom_event.h" #include "../helpers/camera_suite_custom_event.h"
#include <furi.h> #include <furi.h>
#include <furi_hal.h> #include <furi_hal.h>
@@ -19,48 +21,60 @@
#define UART_CH \ #define UART_CH \
(xtreme_settings.uart_esp_channel == UARTDefault ? FuriHalUartIdUSART1 : FuriHalUartIdLPUART1) (xtreme_settings.uart_esp_channel == UARTDefault ? FuriHalUartIdUSART1 : FuriHalUartIdLPUART1)
#pragma once #define BITMAP_HEADER_LENGTH 62
#define FRAME_WIDTH 128
#define FRAME_HEIGHT 64
#define FRAME_BIT_DEPTH 1 #define FRAME_BIT_DEPTH 1
#define FRAME_BUFFER_LENGTH 1024 #define FRAME_BUFFER_LENGTH 1024
#define ROW_BUFFER_LENGTH 16 #define FRAME_HEIGHT 64
#define RING_BUFFER_LENGTH 19 #define FRAME_WIDTH 128
#define HEADER_LENGTH 3 // 'Y', ':', and row identifier
#define LAST_ROW_INDEX 1008 #define LAST_ROW_INDEX 1008
#define RING_BUFFER_LENGTH 19
#define ROW_BUFFER_LENGTH 16
static const unsigned char bitmap_header[BITMAP_HEADER_LENGTH] = {
0x42, 0x4D, 0x3E, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x00, 0x28, 0x00,
0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0x00};
extern const Icon I_DolphinCommon_56x48; extern const Icon I_DolphinCommon_56x48;
typedef struct UartDumpModel UartDumpModel;
struct UartDumpModel {
bool initialized;
int rotation_angle;
uint8_t pixels[FRAME_BUFFER_LENGTH];
uint8_t ringbuffer_index;
uint8_t row_ringbuffer[RING_BUFFER_LENGTH];
};
typedef struct CameraSuiteViewCamera CameraSuiteViewCamera;
typedef void (*CameraSuiteViewCameraCallback)(CameraSuiteCustomEvent event, void* context);
void camera_suite_view_camera_set_callback(
CameraSuiteViewCamera* camera_suite_view_camera,
CameraSuiteViewCameraCallback callback,
void* context);
CameraSuiteViewCamera* camera_suite_view_camera_alloc();
void camera_suite_view_camera_free(CameraSuiteViewCamera* camera_suite_static);
View* camera_suite_view_camera_get_view(CameraSuiteViewCamera* camera_suite_static);
typedef enum { typedef enum {
// Reserved for StreamBuffer internal event WorkerEventReserved = (1 << 0), // Reserved for StreamBuffer internal event
WorkerEventReserved = (1 << 0),
WorkerEventStop = (1 << 1), WorkerEventStop = (1 << 1),
WorkerEventRx = (1 << 2), WorkerEventRx = (1 << 2),
} WorkerEventFlags; } WorkerEventFlags;
#define WORKER_EVENTS_MASK (WorkerEventStop | WorkerEventRx) #define WORKER_EVENTS_MASK (WorkerEventStop | WorkerEventRx)
// Forward declaration
typedef void (*CameraSuiteViewCameraCallback)(CameraSuiteCustomEvent event, void* context);
typedef struct CameraSuiteViewCamera {
CameraSuiteViewCameraCallback callback;
FuriStreamBuffer* rx_stream;
FuriThread* worker_thread;
NotificationApp* notification;
View* view;
void* context;
} CameraSuiteViewCamera;
typedef struct UartDumpModel {
bool flash;
bool initialized;
bool inverted;
int rotation_angle;
uint32_t orientation;
uint8_t pixels[FRAME_BUFFER_LENGTH];
uint8_t ringbuffer_index;
uint8_t row_identifier;
uint8_t row_ringbuffer[RING_BUFFER_LENGTH];
} UartDumpModel;
// Function Prototypes
CameraSuiteViewCamera* camera_suite_view_camera_alloc();
View* camera_suite_view_camera_get_view(CameraSuiteViewCamera* camera_suite_static);
void camera_suite_view_camera_free(CameraSuiteViewCamera* camera_suite_static);
void camera_suite_view_camera_set_callback(
CameraSuiteViewCamera* camera_suite_view_camera,
CameraSuiteViewCameraCallback callback,
void* context);

View File

@@ -32,12 +32,11 @@ void camera_suite_view_guide_draw(Canvas* canvas, CameraSuiteViewGuideModel* mod
canvas_set_font(canvas, FontPrimary); canvas_set_font(canvas, FontPrimary);
canvas_draw_str_aligned(canvas, 0, 0, AlignLeft, AlignTop, "Guide"); canvas_draw_str_aligned(canvas, 0, 0, AlignLeft, AlignTop, "Guide");
canvas_set_font(canvas, FontSecondary); canvas_set_font(canvas, FontSecondary);
canvas_draw_str_aligned(canvas, 0, 12, AlignLeft, AlignTop, "Left = Toggle Invert"); canvas_draw_str_aligned(canvas, 0, 12, AlignLeft, AlignTop, "Left = Toggle invert");
canvas_draw_str_aligned(canvas, 0, 22, AlignLeft, AlignTop, "Right = Toggle Dithering"); canvas_draw_str_aligned(canvas, 0, 22, AlignLeft, AlignTop, "Right = Toggle dithering");
canvas_draw_str_aligned(canvas, 0, 32, AlignLeft, AlignTop, "Up = Contrast Up"); canvas_draw_str_aligned(canvas, 0, 32, AlignLeft, AlignTop, "Up = Contrast up");
canvas_draw_str_aligned(canvas, 0, 42, AlignLeft, AlignTop, "Down = Contrast Down"); canvas_draw_str_aligned(canvas, 0, 42, AlignLeft, AlignTop, "Down = Contrast down");
// TODO: Possibly update to take picture instead. canvas_draw_str_aligned(canvas, 0, 52, AlignLeft, AlignTop, "Center = Take picture");
canvas_draw_str_aligned(canvas, 0, 52, AlignLeft, AlignTop, "Center = Toggle Dither Type");
} }
static void camera_suite_view_guide_model_init(CameraSuiteViewGuideModel* const model) { static void camera_suite_view_guide_model_init(CameraSuiteViewGuideModel* const model) {

View File

@@ -7,6 +7,7 @@ App(
"gui", "gui",
], ],
stack_size=4 * 1024, stack_size=4 * 1024,
order=10,
fap_icon="flipchess_10px.png", fap_icon="flipchess_10px.png",
fap_icon_assets="icons", fap_icon_assets="icons",
fap_icon_assets_symbol="flipchess", fap_icon_assets_symbol="flipchess",

View File

@@ -4,6 +4,7 @@
#include <input/input.h> #include <input/input.h>
#include <gui/elements.h> #include <gui/elements.h>
#include "flipchess_icons.h" #include "flipchess_icons.h"
#include <assets_icons.h> #include <assets_icons.h>
struct FlipChessStartscreen { struct FlipChessStartscreen {

View File

@@ -1,6 +1,6 @@
App( App(
appid="cli_gui", appid="cli_gui",
name="CLI (subghz chat)", name="CLI-GUI Bridge",
apptype=FlipperAppType.EXTERNAL, apptype=FlipperAppType.EXTERNAL,
entry_point="cligui_main", entry_point="cligui_main",
requires=["gui", "cli"], requires=["gui", "cli"],

View File

@@ -12,6 +12,7 @@ volatile bool gotCallbackSet = false;
FuriStreamBuffer* tx_stream; FuriStreamBuffer* tx_stream;
FuriStreamBuffer* rx_stream; FuriStreamBuffer* rx_stream;
static FuriThread* volatile cliThread = NULL; static FuriThread* volatile cliThread = NULL;
static FuriThread* prev_appthread = NULL;
static void tx_handler_stdout(const char* buffer, size_t size) { static void tx_handler_stdout(const char* buffer, size_t size) {
furi_stream_buffer_send(tx_stream, buffer, size, FuriWaitForever); furi_stream_buffer_send(tx_stream, buffer, size, FuriWaitForever);
} }
@@ -72,6 +73,11 @@ void latch_tx_handler() {
session.is_connected = &session_connected; session.is_connected = &session_connected;
cli_session_close(global_cli); cli_session_close(global_cli);
cli_session_open(global_cli, &session); cli_session_open(global_cli, &session);
// Unlock loader-lock
Loader* loader = furi_record_open(RECORD_LOADER);
prev_appthread = loader->app.thread;
loader->app.thread = NULL;
furi_record_close(RECORD_LOADER);
furi_record_close(RECORD_CLI); furi_record_close(RECORD_CLI);
} }
void unlatch_tx_handler(bool persist) { void unlatch_tx_handler(bool persist) {
@@ -101,4 +107,8 @@ void unlatch_tx_handler(bool persist) {
// At this point, all cli_vcp functions should be back. // At this point, all cli_vcp functions should be back.
furi_stream_buffer_free(rx_stream); furi_stream_buffer_free(rx_stream);
furi_stream_buffer_free(tx_stream); furi_stream_buffer_free(tx_stream);
// Re-lock loader (to avoid crash on automatic unlock)
Loader* loader = furi_record_open(RECORD_LOADER);
loader->app.thread = prev_appthread;
furi_record_close(RECORD_LOADER);
} }

Some files were not shown because too many files have changed in this diff Show More