unleashed-firmware/wiki/examples/IPC-example.md
coreglitch 5b6ab7faf3
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
2020-09-01 13:34:23 +03:00

145 lines
4.1 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

In this example we show how to interact data between different applications.
As we already know, we can use FURI Record for this purpose: one application create named record and other application opens it by name.
We will simulate the display. The first application (called `application_ipc_display`) will be display driver, and the second app (called `application_ipc_widget`) will be draw such simple demo on the screen.
# Dsiplay definition
For work with the display we create simple framebuffer and write some "pixels" into it.
```C
#define FB_WIDTH 10
#define FB_HEIGHT 3
#define FB_SIZE (FB_WIDTH * FB_HEIGHT)
char _framebuffer[FB_SIZE];
for(size_t i = 0; i < FB_SIZE; i++) {
_framebuffer[i] = ' ';
}
```
On local target we just draw framebuffer content like this:
```
+==========+
| |
| |
| |
+==========+
```
```C
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");
```
_Notice: after creating display emulator this example should be changed to work with real 128×64 display using u8g2_
# Demo "widget" application
The application opens record with framebuffer:
```C
FuriRecordSubscriber* fb_record = furi_open(
"test_fb", false, false, NULL, NULL, NULL
);
```
Then it clear display and draw "pixel" every 120 ms, and pixel move across the screen.
For do that we should tale framebuffer:
`char* fb = (char*)furi_take(fb_record);`
Write some data:
```C
if(fb == NULL) furiac_exit(NULL);
for(size_t i = 0; i < FB_SIZE; i++) {
fb[i] = ' ';
}
fb[counter % FB_SIZE] = '#';
```
And give framebuffer (use `furi_commit` to notify another apps that record was changed):
`furi_commit(fb_record);`
`counter` is increasing on every iteration to make "pixel" moving.
# Display driver
The driver application creates framebuffer after start (see [Display definition](#Display-definition)) and creates new FURI record with framebuffer pointer:
`furi_create("test_fb", (void*)_framebuffer, FB_SIZE)`
Next it opens this record and subscribe to its changing:
```
FuriRecordSubscriber* fb_record = furi_open(
"test_fb", false, false, handle_fb_change, NULL, &ctx
);
```
The handler is called any time when some app writes to framebuffer record (by calling `furi_commit`):
```C
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
}
```
That callback execute in calling thread/context, so handler pass control flow to app thread by semaphore. App thread wait for semaphore and then do the "rendering":
```C
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
}
```
A structure containing the context is used so that the callback can access the semaphore. This structure is passed as an argument to `furi_open` and goes to the handler:
```C
typedef struct {
SemaphoreHandle_t events; // queue to pass events from callback to app thread
FuriRecordSubscriber* log; // app logger
} IpcCtx;
```
We just have to create a semaphore and define a context:
```C
StaticSemaphore_t event_descriptor;
// create stack-based counting semaphore
SemaphoreHandle_t events = xSemaphoreCreateCountingStatic(255, 0, &event_descriptor);
IpcCtx ctx = {.events = events, .log = log};
```
You can find full example code in `applications/examples/ipc.c`, and run it by `docker-compose exec dev make -C target_lo example_ipc`.
![](https://github.com/Flipper-Zero/flipperzero-firmware-community/raw/master/wiki_static/application_examples/example_ipc.gif)