Example ipc (#60)
* add blank example * add ipc example code, need to change FURI API * add ipc example code, need to change FURI API * change core API, add context * check handler at take * fix important bugs in furi * drawing example * add posix mq * fix unsigned demo counter * create at open * working local demo * russian version of IPC example * english version * add gif
This commit is contained in:
158
applications/examples/ipc.c
Normal file
158
applications/examples/ipc.c
Normal file
@@ -0,0 +1,158 @@
|
||||
#include "flipper.h"
|
||||
#include <string.h>
|
||||
|
||||
#define FB_WIDTH 10
|
||||
#define FB_HEIGHT 3
|
||||
#define FB_SIZE (FB_WIDTH * FB_HEIGHT)
|
||||
|
||||
// context structure used for pass some object from app thread to callback
|
||||
typedef struct {
|
||||
SemaphoreHandle_t events; // queue to pass events from callback to app thread
|
||||
FuriRecordSubscriber* log; // app logger
|
||||
} IpcCtx;
|
||||
|
||||
static void handle_fb_change(const void* fb, size_t fb_size, void* raw_ctx) {
|
||||
IpcCtx* ctx = (IpcCtx*)raw_ctx; // make right type
|
||||
|
||||
fuprintf(ctx->log, "[cb] framebuffer updated\n");
|
||||
|
||||
// send event to app thread
|
||||
xSemaphoreGive(ctx->events);
|
||||
|
||||
// Attention! Please, do not make blocking operation like IO and waits inside callback
|
||||
// Remember that callback execute in calling thread/context
|
||||
}
|
||||
|
||||
static void print_fb(char* fb, FuriRecordSubscriber* log) {
|
||||
if(fb == NULL) return;
|
||||
|
||||
/* draw framebuffer like this:
|
||||
+==========+
|
||||
| |
|
||||
| |
|
||||
| |
|
||||
+==========+
|
||||
*/
|
||||
|
||||
char row_buffer[FB_WIDTH + 1];
|
||||
row_buffer[FB_WIDTH] = '\0';
|
||||
|
||||
// FB layout is hardcoded here
|
||||
fuprintf(log, "+==========+\n");
|
||||
for(uint8_t i = 0; i < FB_HEIGHT; i++) {
|
||||
strncpy(row_buffer, &fb[FB_WIDTH * i], FB_WIDTH);
|
||||
fuprintf(log, "|%s|\n", row_buffer);
|
||||
}
|
||||
fuprintf(log, "+==========+\n");
|
||||
}
|
||||
|
||||
void application_ipc_display(void* p) {
|
||||
// get logger
|
||||
FuriRecordSubscriber* log = get_default_log();
|
||||
|
||||
// create ASCII "framebuffer"
|
||||
// FB_WIDTH x FB_HEIGHT char buffer
|
||||
char _framebuffer[FB_SIZE];
|
||||
|
||||
// init framebuffer by spaces
|
||||
for(size_t i = 0; i < FB_SIZE; i++) {
|
||||
_framebuffer[i] = ' ';
|
||||
}
|
||||
|
||||
// create record
|
||||
if(!furi_create("test_fb", (void*)_framebuffer, FB_SIZE)) {
|
||||
fuprintf(log, "[display] cannot create fb record\n");
|
||||
furiac_exit(NULL);
|
||||
}
|
||||
|
||||
StaticSemaphore_t event_descriptor;
|
||||
// create stack-based counting semaphore
|
||||
SemaphoreHandle_t events = xSemaphoreCreateCountingStatic(255, 0, &event_descriptor);
|
||||
|
||||
if(events == NULL) {
|
||||
fuprintf(log, "[display] cannot create event semaphore\n");
|
||||
furiac_exit(NULL);
|
||||
}
|
||||
|
||||
// save log and event queue in context structure
|
||||
IpcCtx ctx = {.events = events, .log = log};
|
||||
|
||||
// subscribe to record. ctx will be passed to handle_fb_change
|
||||
FuriRecordSubscriber* fb_record = furi_open(
|
||||
"test_fb", false, false, handle_fb_change, NULL, &ctx
|
||||
);
|
||||
|
||||
if(fb_record == NULL) {
|
||||
fuprintf(log, "[display] cannot open fb record\n");
|
||||
furiac_exit(NULL);
|
||||
}
|
||||
|
||||
#ifdef HW_DISPLAY
|
||||
// on Flipper target -- open screen
|
||||
|
||||
// draw border
|
||||
|
||||
#else
|
||||
// on Local target -- print "blank screen"
|
||||
{
|
||||
void* fb = furi_take(fb_record);
|
||||
print_fb((char*)fb, log);
|
||||
furi_give(fb_record);
|
||||
}
|
||||
#endif
|
||||
|
||||
while(1) {
|
||||
// wait for event
|
||||
if(xSemaphoreTake(events, portMAX_DELAY) == pdTRUE) {
|
||||
fuprintf(log, "[display] get fb update\n\n");
|
||||
|
||||
#ifdef HW_DISPLAY
|
||||
// on Flipper target draw the screen
|
||||
#else
|
||||
// on local target just print
|
||||
{
|
||||
void* fb = furi_take(fb_record);
|
||||
print_fb((char*)fb, log);
|
||||
furi_give(fb_record);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Widget application
|
||||
void application_ipc_widget(void* p) {
|
||||
FuriRecordSubscriber* log = get_default_log();
|
||||
|
||||
// open record
|
||||
FuriRecordSubscriber* fb_record = furi_open(
|
||||
"test_fb", false, false, NULL, NULL, NULL
|
||||
);
|
||||
|
||||
if(fb_record == NULL) {
|
||||
fuprintf(log, "[widget] cannot create fb record\n");
|
||||
furiac_exit(NULL);
|
||||
}
|
||||
|
||||
uint8_t counter = 0;
|
||||
|
||||
while(1) {
|
||||
delay(120);
|
||||
|
||||
// write some ascii demo here: '#'' symbol run on overall screen
|
||||
char* fb = (char*)furi_take(fb_record);
|
||||
|
||||
if(fb == NULL) furiac_exit(NULL);
|
||||
|
||||
for(size_t i = 0; i < FB_SIZE; i++) {
|
||||
fb[i] = ' ';
|
||||
}
|
||||
|
||||
fb[counter % FB_SIZE] = '#';
|
||||
|
||||
furi_commit(fb_record);
|
||||
|
||||
counter++;
|
||||
}
|
||||
}
|
@@ -13,6 +13,8 @@ void flipper_test_app(void* p);
|
||||
|
||||
void application_blink(void* p);
|
||||
void application_uart_write(void* p);
|
||||
void application_ipc_display(void* p);
|
||||
void application_ipc_widget(void* p);
|
||||
|
||||
const FlipperStartupApp FLIPPER_STARTUP[] = {
|
||||
#ifdef TEST
|
||||
@@ -25,4 +27,8 @@ const FlipperStartupApp FLIPPER_STARTUP[] = {
|
||||
#ifdef EXAMPLE_UART_WRITE
|
||||
{.app = application_uart_write, .name = "uart write"},
|
||||
#endif
|
||||
#ifdef EXAMPLE_IPC
|
||||
{.app = application_ipc_display, .name = "ipc display"},
|
||||
{.app = application_ipc_widget, .name = "ipc widget"},
|
||||
#endif
|
||||
};
|
@@ -17,7 +17,7 @@ TEST: pipe record
|
||||
|
||||
static uint8_t pipe_record_value = 0;
|
||||
|
||||
void pipe_record_cb(const void* value, size_t size) {
|
||||
void pipe_record_cb(const void* value, size_t size, void* ctx) {
|
||||
// hold value to static var
|
||||
pipe_record_value = *((uint8_t*)value);
|
||||
}
|
||||
@@ -31,7 +31,7 @@ bool test_furi_pipe_record(FuriRecordSubscriber* log) {
|
||||
|
||||
// 2. Open/subscribe to it
|
||||
FuriRecordSubscriber* pipe_record = furi_open(
|
||||
"test/pipe", false, false, pipe_record_cb, NULL
|
||||
"test/pipe", false, false, pipe_record_cb, NULL, NULL
|
||||
);
|
||||
if(pipe_record == NULL) {
|
||||
fuprintf(log, "cannot open record\n");
|
||||
@@ -83,7 +83,7 @@ TEST: holding data
|
||||
|
||||
static uint8_t holding_record_value = 0;
|
||||
|
||||
void holding_record_cb(const void* value, size_t size) {
|
||||
void holding_record_cb(const void* value, size_t size, void* ctx) {
|
||||
// hold value to static var
|
||||
holding_record_value = *((uint8_t*)value);
|
||||
}
|
||||
@@ -98,7 +98,7 @@ bool test_furi_holding_data(FuriRecordSubscriber* log) {
|
||||
|
||||
// 2. Open/Subscribe on it
|
||||
FuriRecordSubscriber* holding_record = furi_open(
|
||||
"test/holding", false, false, holding_record_cb, NULL
|
||||
"test/holding", false, false, holding_record_cb, NULL, NULL
|
||||
);
|
||||
if(holding_record == NULL) {
|
||||
fuprintf(log, "cannot open record\n");
|
||||
@@ -164,7 +164,7 @@ void furi_concurent_app(void* p) {
|
||||
FuriRecordSubscriber* log = (FuriRecordSubscriber*)p;
|
||||
|
||||
FuriRecordSubscriber* holding_record = furi_open(
|
||||
"test/concurrent", false, false, NULL, NULL
|
||||
"test/concurrent", false, false, NULL, NULL, NULL
|
||||
);
|
||||
if(holding_record == NULL) {
|
||||
fuprintf(log, "cannot open record\n");
|
||||
@@ -203,7 +203,7 @@ bool test_furi_concurrent_access(FuriRecordSubscriber* log) {
|
||||
|
||||
// 2. Open it
|
||||
FuriRecordSubscriber* holding_record = furi_open(
|
||||
"test/concurrent", false, false, NULL, NULL
|
||||
"test/concurrent", false, false, NULL, NULL, NULL
|
||||
);
|
||||
if(holding_record == NULL) {
|
||||
fuprintf(log, "cannot open record\n");
|
||||
@@ -307,12 +307,12 @@ TODO: test 7 not pass beacuse cleanup is not implemented
|
||||
static uint8_t mute_last_value = 0;
|
||||
static FlipperRecordState mute_last_state = 255;
|
||||
|
||||
void mute_record_cb(const void* value, size_t size) {
|
||||
void mute_record_cb(const void* value, size_t size, void* ctx) {
|
||||
// hold value to static var
|
||||
mute_last_value = *((uint8_t*)value);
|
||||
}
|
||||
|
||||
void mute_record_state_cb(FlipperRecordState state) {
|
||||
void mute_record_state_cb(FlipperRecordState state, void* ctx) {
|
||||
mute_last_state = state;
|
||||
}
|
||||
|
||||
@@ -327,7 +327,7 @@ void furi_mute_parent_app(void* p) {
|
||||
|
||||
// 2. Open watch handler: solo=false, no_mute=false, subscribe to data
|
||||
FuriRecordSubscriber* watch_handler = furi_open(
|
||||
"test/mute", false, false, mute_record_cb, NULL
|
||||
"test/mute", false, false, mute_record_cb, NULL, NULL
|
||||
);
|
||||
if(watch_handler == NULL) {
|
||||
fuprintf(log, "cannot open watch handler\n");
|
||||
@@ -350,7 +350,7 @@ bool test_furi_mute_algorithm(FuriRecordSubscriber* log) {
|
||||
|
||||
// 2. Open handler A: solo=false, no_mute=false, NULL subscriber. Subscribe to state.
|
||||
FuriRecordSubscriber* handler_a = furi_open(
|
||||
"test/mute", false, false, NULL, mute_record_state_cb
|
||||
"test/mute", false, false, NULL, mute_record_state_cb, NULL
|
||||
);
|
||||
if(handler_a == NULL) {
|
||||
fuprintf(log, "cannot open handler A\n");
|
||||
@@ -372,7 +372,7 @@ bool test_furi_mute_algorithm(FuriRecordSubscriber* log) {
|
||||
|
||||
// 3. Open handler B: solo=true, no_mute=true, NULL subscriber.
|
||||
FuriRecordSubscriber* handler_b = furi_open(
|
||||
"test/mute", true, true, NULL, NULL
|
||||
"test/mute", true, true, NULL, NULL, NULL
|
||||
);
|
||||
if(handler_b == NULL) {
|
||||
fuprintf(log, "cannot open handler B\n");
|
||||
@@ -415,7 +415,7 @@ bool test_furi_mute_algorithm(FuriRecordSubscriber* log) {
|
||||
|
||||
// 4. Open hadler C: solo=true, no_mute=false, NULL subscriber.
|
||||
FuriRecordSubscriber* handler_c = furi_open(
|
||||
"test/mute", true, false, NULL, NULL
|
||||
"test/mute", true, false, NULL, NULL, NULL
|
||||
);
|
||||
if(handler_c == NULL) {
|
||||
fuprintf(log, "cannot open handler C\n");
|
||||
@@ -428,7 +428,7 @@ bool test_furi_mute_algorithm(FuriRecordSubscriber* log) {
|
||||
|
||||
// 5. Open handler D: solo=false, no_mute=false, NULL subscriber.
|
||||
FuriRecordSubscriber* handler_d = furi_open(
|
||||
"test/mute", false, false, NULL, NULL
|
||||
"test/mute", false, false, NULL, NULL, NULL
|
||||
);
|
||||
if(handler_d == NULL) {
|
||||
fuprintf(log, "cannot open handler D\n");
|
||||
|
Reference in New Issue
Block a user