Merge branch 'release-candidate' into release

This commit is contained in:
Aleksandr Kutuzov 2021-09-15 20:27:06 +03:00
commit fd29e190b7
331 changed files with 33284 additions and 4792 deletions

View File

@ -10,7 +10,8 @@ on:
pull_request:
env:
TARGETS: f6
TARGETS: f6 f7
DEFAULT_TARGET: f6
jobs:
build:
@ -69,7 +70,7 @@ jobs:
echo "::set-output name=artifacts-path::${BRANCH_OR_TAG}"
echo "::set-output name=suffix::${SUFFIX}"
echo "::set-output name=short-hash::${SHA}"
echo "::set-output name=latest-target::${TARGETS[${#TARGETS[@]}-1]}"
echo "::set-output name=default-target::${DEFAULT_TARGET}"
- name: 'Build bootloader in docker'
uses: ./.github/actions/docker
@ -169,12 +170,7 @@ jobs:
run: |
test -d core2_firmware && rm -rf core2_firmware || true
mkdir core2_firmware
cp \
lib/STM32CubeWB/package.xml \
lib/STM32CubeWB/Projects/STM32WB_Copro_Wireless_Binaries/STM32WB5x/stm32wb5x_FUS_fw.bin \
lib/STM32CubeWB/Projects/STM32WB_Copro_Wireless_Binaries/STM32WB5x/stm32wb5x_FUS_fw_for_fus_0_5_3.bin \
lib/STM32CubeWB/Projects/STM32WB_Copro_Wireless_Binaries/STM32WB5x/stm32wb5x_BLE_Stack_full_fw.bin \
core2_firmware
./scripts/assets.py copro lib/STM32CubeWB core2_firmware STM32WB5x
tar czpf artifacts/flipper-z-any-core2_firmware-${{steps.names.outputs.suffix}}.tgz core2_firmware
- name: 'Bundle scripts'
@ -185,6 +181,7 @@ jobs:
- name: 'Bundle resources'
if: ${{ !github.event.pull_request.head.repo.fork }}
run: |
./scripts/assets.py manifest assets/resources
tar czpf artifacts/flipper-z-any-resources-${{steps.names.outputs.suffix}}.tgz -C assets resources
- name: 'Upload artifacts to update server'
@ -205,7 +202,7 @@ jobs:
with:
args: -X POST -F 'key=${{ secrets.REINDEX_KEY }}' ${{ secrets.REINDEX_URL }}
- name: Find Previous Comment
- name: 'Find Previous Comment'
if: ${{ !github.event.pull_request.head.repo.fork && github.event.pull_request }}
uses: peter-evans/find-comment@v1
id: fc
@ -214,12 +211,12 @@ jobs:
comment-author: 'github-actions[bot]'
body-includes: 'to flash the'
- name: Create or update comment
- name: 'Create or update comment'
if: ${{ !github.event.pull_request.head.repo.fork && github.event.pull_request}}
uses: peter-evans/create-or-update-comment@v1
with:
comment-id: ${{ steps.fc.outputs.comment-id }}
issue-number: ${{ github.event.pull_request.number }}
body: |
[Click here](https://update.flipperzero.one/?url=https://update.flipperzero.one/builds/firmware/${{steps.names.outputs.artifacts-path}}/flipper-z-${{steps.names.outputs.latest-target}}-full-${{steps.names.outputs.suffix}}.dfu&channel=${{steps.names.outputs.artifacts-path}}&version=${{steps.names.outputs.short-hash}}) to flash the `${{steps.names.outputs.short-hash}}` version of this branch via WebUSB.
[Click here](https://update.flipperzero.one/?url=https://update.flipperzero.one/builds/firmware/${{steps.names.outputs.artifacts-path}}/flipper-z-${{steps.names.outputs.default-target}}-full-${{steps.names.outputs.suffix}}.dfu&channel=${{steps.names.outputs.artifacts-path}}&version=${{steps.names.outputs.short-hash}}) to flash the `${{steps.names.outputs.short-hash}}` version of this branch via WebUSB.
edit-mode: replace

View File

@ -10,7 +10,7 @@ on:
pull_request:
env:
TARGETS: f6
TARGETS: f6 f7
jobs:
lint_c_cpp:

View File

@ -14,7 +14,7 @@ Our goal is to create nice and clean code with good documentation, to make it a
Flipper Zero's firmware consists of three components:
- Core2 firmware set - proprietary componenets by ST: FUS + radio stack.
- Core2 firmware set - proprietary components by ST: FUS + radio stack.
- Core1 Bootloader - controls basic hardware initialization and loads firmware
- Core1 Firmware - HAL + OS + Drivers + Applications
@ -67,13 +67,13 @@ One liner: `./flash_core1_main.sh`
```
3. Prepare the container:
```sh
docker compose up -d
docker-compose up -d
```
## Compile bootloader
```sh
docker compose exec dev make -j$(nproc) -C bootloader TARGET=f6
docker-compose exec dev make -j$(nproc) -C bootloader TARGET=f6
```
Bootloader compilation results:
@ -85,7 +85,7 @@ Bootloader compilation results:
## Compile firmware
```sh
docker compose exec dev make -j$(nproc) -C firmware TARGET=f6
docker-compose exec dev make -j$(nproc) -C firmware TARGET=f6
```
Firmware compilation results:
@ -102,14 +102,14 @@ That's exactly how we generate our `full` builds.
1. Concatenate HEX files:
```sh
docker compose exec dev srec_cat \
docker-compose exec dev srec_cat \
bootloader/.obj/f6/bootloader.hex -Intel \
firmware/.obj/f6/firmware.hex -Intel \
-o firmware/.obj/f6/full.hex -Intel
```
2. Convert HEX to DFU:
```sh
docker compose exec dev hex2dfu \
docker-compose exec dev hex2dfu \
-i firmware/.obj/f6/full.hex \
-o firmware/.obj/f6/full.dfu \
-l "Flipper Zero F6"

View File

@ -1,831 +1,69 @@
#include "archive_i.h"
static bool archive_get_filenames(ArchiveApp* archive);
static void update_offset(ArchiveApp* archive) {
furi_assert(archive);
with_view_model(
archive->view_archive_main, (ArchiveViewModel * model) {
size_t array_size = files_array_size(model->files);
uint16_t bounds = array_size > 3 ? 2 : array_size;
if(array_size > 3 && model->idx >= array_size - 1) {
model->list_offset = model->idx - 3;
} else if(model->list_offset < model->idx - bounds) {
model->list_offset = CLAMP(model->list_offset + 1, array_size - bounds, 0);
} else if(model->list_offset > model->idx - bounds) {
model->list_offset = CLAMP(model->idx - 1, array_size - bounds, 0);
}
return true;
});
}
static void archive_update_last_idx(ArchiveApp* archive) {
furi_assert(archive);
with_view_model(
archive->view_archive_main, (ArchiveViewModel * model) {
archive->browser.last_idx[archive->browser.depth] =
CLAMP(model->idx, files_array_size(model->files) - 1, 0);
model->idx = 0;
return true;
});
}
static void archive_switch_dir(ArchiveApp* archive, const char* path) {
furi_assert(archive);
furi_assert(path);
string_set(archive->browser.path, path);
archive_get_filenames(archive);
update_offset(archive);
}
static void archive_switch_tab(ArchiveApp* archive) {
furi_assert(archive);
with_view_model(
archive->view_archive_main, (ArchiveViewModel * model) {
model->tab_idx = archive->browser.tab_id;
model->idx = 0;
return true;
});
archive->browser.depth = 0;
archive_switch_dir(archive, tab_default_paths[archive->browser.tab_id]);
}
static void archive_leave_dir(ArchiveApp* archive) {
furi_assert(archive);
char* last_char_ptr = strrchr(string_get_cstr(archive->browser.path), '/');
if(last_char_ptr) {
size_t pos = last_char_ptr - string_get_cstr(archive->browser.path);
string_left(archive->browser.path, pos);
}
archive->browser.depth = CLAMP(archive->browser.depth - 1, MAX_DEPTH, 0);
with_view_model(
archive->view_archive_main, (ArchiveViewModel * model) {
model->idx = archive->browser.last_idx[archive->browser.depth];
model->list_offset =
model->idx -
(files_array_size(model->files) > 3 ? 3 : files_array_size(model->files));
return true;
});
archive_switch_dir(archive, string_get_cstr(archive->browser.path));
update_offset(archive);
}
static void archive_enter_dir(ArchiveApp* archive, string_t name) {
furi_assert(archive);
furi_assert(name);
archive_update_last_idx(archive);
archive->browser.depth = CLAMP(archive->browser.depth + 1, MAX_DEPTH, 0);
string_cat(archive->browser.path, "/");
string_cat(archive->browser.path, archive->browser.name);
archive_switch_dir(archive, string_get_cstr(archive->browser.path));
}
static bool filter_by_extension(ArchiveApp* archive, FileInfo* file_info, const char* name) {
furi_assert(archive);
furi_assert(file_info);
furi_assert(name);
bool result = false;
const char* filter_ext_ptr = get_tab_ext(archive->browser.tab_id);
if(strcmp(filter_ext_ptr, "*") == 0) {
result = true;
} else if(strstr(name, filter_ext_ptr) != NULL) {
result = true;
} else if(file_info->flags & FSF_DIRECTORY) {
result = true;
}
return result;
}
static void set_file_type(ArchiveFile_t* file, FileInfo* file_info) {
furi_assert(file);
furi_assert(file_info);
for(size_t i = 0; i < SIZEOF_ARRAY(known_ext); i++) {
if(string_search_str(file->name, known_ext[i], 0) != STRING_FAILURE) {
file->type = i;
return;
}
}
if(file_info->flags & FSF_DIRECTORY) {
file->type = ArchiveFileTypeFolder;
} else {
file->type = ArchiveFileTypeUnknown;
}
}
static void archive_file_append(ArchiveApp* archive, const char* path, string_t string) {
furi_assert(archive);
furi_assert(path);
furi_assert(string);
FileWorker* file_worker = file_worker_alloc(false);
if(!file_worker_open(file_worker, path, FSAM_WRITE, FSOM_OPEN_APPEND)) {
FURI_LOG_E("Archive", "Append open error");
}
if(!file_worker_write(file_worker, string_get_cstr(string), string_size(string))) {
FURI_LOG_E("Archive", "Append write error");
}
file_worker_close(file_worker);
file_worker_free(file_worker);
}
static void archive_view_add_item(ArchiveApp* archive, FileInfo* file_info, const char* name) {
furi_assert(archive);
furi_assert(file_info);
furi_assert(name);
ArchiveFile_t item;
if(filter_by_extension(archive, file_info, name)) {
ArchiveFile_t_init(&item);
string_init_set_str(item.name, name);
set_file_type(&item, file_info);
with_view_model(
archive->view_archive_main, (ArchiveViewModel * model) {
files_array_push_back(model->files, item);
return true;
});
ArchiveFile_t_clear(&item);
}
}
static bool archive_is_favorite(ArchiveApp* archive, ArchiveFile_t* selected) {
furi_assert(selected);
string_t path;
string_t buffer;
string_init(buffer);
bool found = false;
string_init_printf(
path, "%s/%s", string_get_cstr(archive->browser.path), string_get_cstr(selected->name));
bool load_result =
file_worker_open(archive->file_worker, ARCHIVE_FAV_PATH, FSAM_READ, FSOM_OPEN_ALWAYS);
if(load_result) {
while(1) {
if(!file_worker_read_until(archive->file_worker, buffer, '\n')) {
break;
}
if(!string_size(buffer)) {
break;
}
if(!string_search(buffer, path)) {
found = true;
break;
}
}
}
string_clear(buffer);
string_clear(path);
file_worker_close(archive->file_worker);
return found;
}
static bool archive_favorites_read(ArchiveApp* archive) {
string_t buffer;
FileInfo file_info;
string_init(buffer);
bool load_result =
file_worker_open(archive->file_worker, ARCHIVE_FAV_PATH, FSAM_READ, FSOM_OPEN_EXISTING);
if(load_result) {
while(1) {
if(!file_worker_read_until(archive->file_worker, buffer, '\n')) {
break;
}
if(!string_size(buffer)) {
break;
}
archive_view_add_item(archive, &file_info, string_get_cstr(buffer));
string_clean(buffer);
}
}
string_clear(buffer);
file_worker_close(archive->file_worker);
return load_result;
}
static bool
archive_favorites_rename(ArchiveApp* archive, ArchiveFile_t* selected, const char* dst) {
furi_assert(selected);
string_t path;
string_t buffer;
string_t temp;
string_init(buffer);
string_init(temp);
string_init_printf(
path, "%s/%s", string_get_cstr(archive->browser.path), string_get_cstr(selected->name));
bool load_result =
file_worker_open(archive->file_worker, ARCHIVE_FAV_PATH, FSAM_READ, FSOM_OPEN_EXISTING);
if(load_result) {
while(1) {
if(!file_worker_read_until(archive->file_worker, buffer, '\n')) {
break;
}
if(!string_size(buffer)) {
break;
}
string_printf(
temp, "%s\r\n", string_search(buffer, path) ? string_get_cstr(buffer) : dst);
archive_file_append(archive, ARCHIVE_FAV_TEMP_PATH, temp);
string_clean(temp);
}
}
string_clear(temp);
string_clear(buffer);
string_clear(path);
file_worker_close(archive->file_worker);
file_worker_remove(archive->file_worker, ARCHIVE_FAV_PATH);
file_worker_rename(archive->file_worker, ARCHIVE_FAV_TEMP_PATH, ARCHIVE_FAV_PATH);
return load_result;
}
static bool archive_favorites_delete(ArchiveApp* archive, ArchiveFile_t* selected) {
furi_assert(selected);
string_t path;
string_t buffer;
string_init(buffer);
string_init_printf(
path, "%s/%s", string_get_cstr(archive->browser.path), string_get_cstr(selected->name));
bool load_result =
file_worker_open(archive->file_worker, ARCHIVE_FAV_PATH, FSAM_READ, FSOM_OPEN_EXISTING);
if(load_result) {
while(1) {
if(!file_worker_read_until(archive->file_worker, buffer, '\n')) {
break;
}
if(!string_size(buffer)) {
break;
}
if(string_search(buffer, path)) {
string_t temp;
string_init_printf(temp, "%s\r\n", string_get_cstr(buffer));
archive_file_append(archive, ARCHIVE_FAV_TEMP_PATH, temp);
string_clear(temp);
}
}
}
string_clear(buffer);
string_clear(path);
file_worker_close(archive->file_worker);
file_worker_remove(archive->file_worker, ARCHIVE_FAV_PATH);
file_worker_rename(archive->file_worker, ARCHIVE_FAV_TEMP_PATH, ARCHIVE_FAV_PATH);
return load_result;
}
static bool archive_read_dir(ArchiveApp* archive) {
FileInfo file_info;
File* directory = storage_file_alloc(archive->api);
char name[MAX_NAME_LEN];
if(!storage_dir_open(directory, string_get_cstr(archive->browser.path))) {
storage_dir_close(directory);
storage_file_free(directory);
return false;
}
while(1) {
if(!storage_dir_read(directory, &file_info, name, MAX_NAME_LEN)) {
break;
}
uint16_t files_cnt;
with_view_model(
archive->view_archive_main, (ArchiveViewModel * model) {
files_cnt = files_array_size(model->files);
return true;
});
if(files_cnt > MAX_FILES) {
break;
} else if(storage_file_get_error(directory) == FSE_OK) {
archive_view_add_item(archive, &file_info, name);
} else {
storage_dir_close(directory);
storage_file_free(directory);
return false;
}
}
storage_dir_close(directory);
storage_file_free(directory);
return true;
}
static bool archive_get_filenames(ArchiveApp* archive) {
furi_assert(archive);
with_view_model(
archive->view_archive_main, (ArchiveViewModel * model) {
files_array_clean(model->files);
return true;
});
if(archive->browser.tab_id != ArchiveTabFavorites) {
archive_read_dir(archive);
} else {
archive_favorites_read(archive);
}
return true;
}
static void archive_exit_callback(ArchiveApp* archive) {
furi_assert(archive);
AppEvent event;
event.type = EventTypeExit;
furi_check(osMessageQueuePut(archive->event_queue, &event, 0, osWaitForever) == osOK);
}
static uint32_t archive_previous_callback(void* context) {
return ArchiveViewMain;
}
/* file menu */
static void archive_add_to_favorites(ArchiveApp* archive) {
furi_assert(archive);
string_t buffer_src;
string_init_printf(
buffer_src,
"%s/%s\r\n",
string_get_cstr(archive->browser.path),
string_get_cstr(archive->browser.name));
archive_file_append(archive, ARCHIVE_FAV_PATH, buffer_src);
string_clear(buffer_src);
}
static void archive_text_input_callback(void* context) {
bool archive_custom_event_callback(void* context, uint32_t event) {
furi_assert(context);
ArchiveApp* archive = (ArchiveApp*)context;
string_t buffer_src;
string_t buffer_dst;
string_init_printf(
buffer_src,
"%s/%s",
string_get_cstr(archive->browser.path),
string_get_cstr(archive->browser.name));
string_init_printf(
buffer_dst,
"%s/%s",
string_get_cstr(archive->browser.path),
archive->browser.text_input_buffer);
string_set(archive->browser.name, archive->browser.text_input_buffer);
// append extension
ArchiveFile_t* file;
with_view_model(
archive->view_archive_main, (ArchiveViewModel * model) {
file = files_array_get(
model->files, CLAMP(model->idx, files_array_size(model->files) - 1, 0));
file->fav = archive_is_favorite(archive, file);
return true;
});
string_cat(buffer_dst, known_ext[file->type]);
storage_common_rename(archive->api, string_get_cstr(buffer_src), string_get_cstr(buffer_dst));
if(file->fav) {
archive_favorites_rename(archive, file, string_get_cstr(buffer_dst));
}
view_dispatcher_switch_to_view(archive->view_dispatcher, ArchiveViewMain);
archive_get_filenames(archive);
with_view_model(
archive->view_archive_main, (ArchiveViewModel * model) {
model->idx = 0;
while(model->idx < files_array_size(model->files)) {
ArchiveFile_t* current = files_array_get(model->files, model->idx);
if(!string_search(current->name, archive->browser.text_input_buffer)) {
break;
}
++model->idx;
}
return true;
});
update_offset(archive);
string_clear(buffer_src);
string_clear(buffer_dst);
return scene_manager_handle_custom_event(archive->scene_manager, event);
}
static void archive_enter_text_input(ArchiveApp* archive) {
furi_assert(archive);
*archive->browser.text_input_buffer = '\0';
strlcpy(
archive->browser.text_input_buffer, string_get_cstr(archive->browser.name), MAX_NAME_LEN);
archive_trim_file_ext(archive->browser.text_input_buffer);
text_input_set_header_text(archive->text_input, "Rename:");
text_input_set_result_callback(
archive->text_input,
archive_text_input_callback,
archive,
archive->browser.text_input_buffer,
MAX_NAME_LEN,
false);
view_dispatcher_switch_to_view(archive->view_dispatcher, ArchiveViewTextInput);
}
static void archive_show_file_menu(ArchiveApp* archive) {
furi_assert(archive);
archive->browser.menu = true;
with_view_model(
archive->view_archive_main, (ArchiveViewModel * model) {
ArchiveFile_t* selected;
selected = files_array_get(model->files, model->idx);
model->menu = true;
model->menu_idx = 0;
selected->fav = is_known_app(selected->type) ? archive_is_favorite(archive, selected) :
false;
return true;
});
}
static void archive_close_file_menu(ArchiveApp* archive) {
furi_assert(archive);
archive->browser.menu = false;
with_view_model(
archive->view_archive_main, (ArchiveViewModel * model) {
model->menu = false;
model->menu_idx = 0;
return true;
});
}
static void archive_open_app(ArchiveApp* archive, const char* app_name, const char* args) {
furi_assert(archive);
furi_assert(app_name);
loader_start(archive->loader, app_name, args);
}
static void archive_delete_file(ArchiveApp* archive, ArchiveFile_t* file) {
furi_assert(archive);
furi_assert(file);
string_t path;
string_init(path);
string_printf(
path, "%s/%s", string_get_cstr(archive->browser.path), string_get_cstr(file->name));
if(archive_is_favorite(archive, file)) { // remove from favorites
archive_favorites_delete(archive, file);
}
file_worker_remove(archive->file_worker, string_get_cstr(path));
string_clear(path);
archive_get_filenames(archive);
with_view_model(
archive->view_archive_main, (ArchiveViewModel * model) {
model->idx = CLAMP(model->idx, files_array_size(model->files) - 1, 0);
return true;
});
update_offset(archive);
}
static void
archive_run_in_app(ArchiveApp* archive, ArchiveFile_t* selected, bool full_path_provided) {
string_t full_path;
if(!full_path_provided) {
string_init_printf(
full_path,
"%s/%s",
string_get_cstr(archive->browser.path),
string_get_cstr(selected->name));
} else {
string_init_set(full_path, selected->name);
}
archive_open_app(archive, flipper_app_name[selected->type], string_get_cstr(full_path));
string_clear(full_path);
}
static void archive_file_menu_callback(ArchiveApp* archive) {
furi_assert(archive);
ArchiveFile_t* selected;
uint8_t idx = 0;
with_view_model(
archive->view_archive_main, (ArchiveViewModel * model) {
selected = files_array_get(model->files, model->idx);
idx = model->menu_idx;
return true;
});
switch(idx) {
case 0:
if(is_known_app(selected->type)) {
archive_run_in_app(archive, selected, false);
}
break;
case 1:
if(is_known_app(selected->type)) {
if(!archive_is_favorite(archive, selected)) {
string_set(archive->browser.name, selected->name);
archive_add_to_favorites(archive);
} else {
// delete from favorites
archive_favorites_delete(archive, selected);
}
archive_close_file_menu(archive);
}
break;
case 2:
// open rename view
if(is_known_app(selected->type)) {
archive_enter_text_input(archive);
}
break;
case 3:
// confirmation?
archive_delete_file(archive, selected);
archive_close_file_menu(archive);
break;
default:
archive_close_file_menu(archive);
break;
}
selected = NULL;
}
static void menu_input_handler(ArchiveApp* archive, InputEvent* event) {
furi_assert(archive);
furi_assert(archive);
if(event->type == InputTypeShort) {
if(event->key == InputKeyUp || event->key == InputKeyDown) {
with_view_model(
archive->view_archive_main, (ArchiveViewModel * model) {
if(event->key == InputKeyUp) {
model->menu_idx = ((model->menu_idx - 1) + MENU_ITEMS) % MENU_ITEMS;
} else if(event->key == InputKeyDown) {
model->menu_idx = (model->menu_idx + 1) % MENU_ITEMS;
}
return true;
});
}
if(event->key == InputKeyOk) {
archive_file_menu_callback(archive);
} else if(event->key == InputKeyBack) {
archive_close_file_menu(archive);
}
}
}
/* main controls */
static bool archive_view_input(InputEvent* event, void* context) {
furi_assert(event);
bool archive_back_event_callback(void* context) {
furi_assert(context);
ArchiveApp* archive = context;
bool in_menu = archive->browser.menu;
if(in_menu) {
menu_input_handler(archive, event);
return true;
}
if(event->type == InputTypeShort) {
if(event->key == InputKeyLeft) {
if(archive->browser.tab_id > 0) {
archive->browser.tab_id = CLAMP(archive->browser.tab_id - 1, ArchiveTabTotal, 0);
archive_switch_tab(archive);
return true;
}
} else if(event->key == InputKeyRight) {
if(archive->browser.tab_id < ArchiveTabTotal - 1) {
archive->browser.tab_id =
CLAMP(archive->browser.tab_id + 1, ArchiveTabTotal - 1, 0);
archive_switch_tab(archive);
return true;
}
} else if(event->key == InputKeyBack) {
if(archive->browser.depth == 0) {
archive_exit_callback(archive);
} else {
archive_leave_dir(archive);
}
return true;
}
}
if(event->key == InputKeyUp || event->key == InputKeyDown) {
with_view_model(
archive->view_archive_main, (ArchiveViewModel * model) {
uint16_t num_elements = (uint16_t)files_array_size(model->files);
if((event->type == InputTypeShort || event->type == InputTypeRepeat)) {
if(event->key == InputKeyUp) {
model->idx = ((model->idx - 1) + num_elements) % num_elements;
} else if(event->key == InputKeyDown) {
model->idx = (model->idx + 1) % num_elements;
}
}
return true;
});
update_offset(archive);
}
if(event->key == InputKeyOk) {
ArchiveFile_t* selected;
with_view_model(
archive->view_archive_main, (ArchiveViewModel * model) {
selected = files_array_size(model->files) > 0 ?
files_array_get(model->files, model->idx) :
NULL;
return true;
});
if(selected) {
string_set(archive->browser.name, selected->name);
if(selected->type == ArchiveFileTypeFolder) {
if(event->type == InputTypeShort) {
archive_enter_dir(archive, archive->browser.name);
} else if(event->type == InputTypeLong) {
archive_show_file_menu(archive);
}
} else {
if(event->type == InputTypeShort) {
if(archive->browser.tab_id == ArchiveTabFavorites) {
if(is_known_app(selected->type)) {
archive_run_in_app(archive, selected, true);
}
} else {
archive_show_file_menu(archive);
}
}
}
}
}
update_offset(archive);
return true;
}
void archive_free(ArchiveApp* archive) {
furi_assert(archive);
file_worker_free(archive->file_worker);
view_dispatcher_remove_view(archive->view_dispatcher, ArchiveViewMain);
view_dispatcher_remove_view(archive->view_dispatcher, ArchiveViewTextInput);
view_dispatcher_free(archive->view_dispatcher);
with_view_model(
archive->view_archive_main, (ArchiveViewModel * model) {
files_array_clear(model->files);
return false;
});
view_free(archive->view_archive_main);
string_clear(archive->browser.name);
string_clear(archive->browser.path);
text_input_free(archive->text_input);
furi_record_close("storage");
archive->api = NULL;
furi_record_close("gui");
archive->gui = NULL;
furi_record_close("loader");
archive->loader = NULL;
furi_thread_free(archive->app_thread);
furi_check(osMessageQueueDelete(archive->event_queue) == osOK);
free(archive);
ArchiveApp* archive = (ArchiveApp*)context;
return scene_manager_handle_back_event(archive->scene_manager);
}
ArchiveApp* archive_alloc() {
ArchiveApp* archive = furi_alloc(sizeof(ArchiveApp));
archive->event_queue = osMessageQueueNew(8, sizeof(AppEvent), NULL);
archive->app_thread = furi_thread_alloc();
archive->gui = furi_record_open("gui");
archive->loader = furi_record_open("loader");
archive->api = furi_record_open("storage");
archive->text_input = text_input_alloc();
archive->view_archive_main = view_alloc();
archive->file_worker = file_worker_alloc(true);
furi_check(archive->event_queue);
view_allocate_model(
archive->view_archive_main, ViewModelTypeLocking, sizeof(ArchiveViewModel));
with_view_model(
archive->view_archive_main, (ArchiveViewModel * model) {
files_array_init(model->files);
return false;
});
view_set_context(archive->view_archive_main, archive);
view_set_draw_callback(archive->view_archive_main, archive_view_render);
view_set_input_callback(archive->view_archive_main, archive_view_input);
view_set_previous_callback(
text_input_get_view(archive->text_input), archive_previous_callback);
// View Dispatcher
archive->view_dispatcher = view_dispatcher_alloc();
view_dispatcher_add_view(
archive->view_dispatcher, ArchiveViewMain, archive->view_archive_main);
view_dispatcher_add_view(
archive->view_dispatcher, ArchiveViewTextInput, text_input_get_view(archive->text_input));
archive->scene_manager = scene_manager_alloc(&archive_scene_handlers, archive);
view_dispatcher_enable_queue(archive->view_dispatcher);
view_dispatcher_attach_to_gui(
archive->view_dispatcher, archive->gui, ViewDispatcherTypeFullscreen);
view_dispatcher_switch_to_view(archive->view_dispatcher, ArchiveTabFavorites);
view_dispatcher_set_event_callback_context(archive->view_dispatcher, archive);
view_dispatcher_set_custom_event_callback(
archive->view_dispatcher, archive_custom_event_callback);
view_dispatcher_set_navigation_event_callback(
archive->view_dispatcher, archive_back_event_callback);
archive->main_view = main_view_alloc();
view_dispatcher_add_view(
archive->view_dispatcher, ArchiveViewBrowser, archive_main_get_view(archive->main_view));
view_dispatcher_add_view(
archive->view_dispatcher, ArchiveViewTextInput, text_input_get_view(archive->text_input));
return archive;
}
void archive_free(ArchiveApp* archive) {
furi_assert(archive);
view_dispatcher_remove_view(archive->view_dispatcher, ArchiveViewBrowser);
view_dispatcher_remove_view(archive->view_dispatcher, ArchiveViewTextInput);
view_dispatcher_free(archive->view_dispatcher);
scene_manager_free(archive->scene_manager);
main_view_free(archive->main_view);
text_input_free(archive->text_input);
furi_record_close("gui");
archive->gui = NULL;
free(archive);
}
int32_t archive_app(void* p) {
ArchiveApp* archive = archive_alloc();
// default tab
archive_switch_tab(archive);
AppEvent event;
while(1) {
furi_check(osMessageQueueGet(archive->event_queue, &event, NULL, osWaitForever) == osOK);
if(event.type == EventTypeExit) {
break;
}
}
scene_manager_next_scene(archive->scene_manager, ArchiveAppSceneBrowser);
view_dispatcher_run(archive->view_dispatcher);
archive_free(archive);
return 0;
}

View File

@ -5,44 +5,27 @@
#include <furi.h>
#include <gui/gui_i.h>
#include <gui/view_dispatcher.h>
#include <gui/scene_manager.h>
#include <gui/modules/text_input.h>
#include <loader/loader.h>
#include <m-string.h>
#include <m-array.h>
#include <storage/storage.h>
#include "archive_views.h"
#include "applications.h"
#include "file-worker.h"
#define MAX_DEPTH 32
#define MAX_FILES 100 //temp
#include "views/archive_main_view.h"
#include "scenes/archive_scene.h"
#define MAX_FILE_SIZE 128
#define ARCHIVE_FAV_PATH "/any/favorites.txt"
#define ARCHIVE_FAV_TEMP_PATH "/any/favorites.tmp"
typedef enum {
ArchiveViewMain,
ArchiveViewBrowser,
ArchiveViewTextInput,
ArchiveViewTotal,
} ArchiveViewEnum;
static const char* flipper_app_name[] = {
[ArchiveFileTypeIButton] = "iButton",
[ArchiveFileTypeNFC] = "NFC",
[ArchiveFileTypeSubGhz] = "Sub-GHz",
[ArchiveFileTypeLFRFID] = "125 kHz RFID",
[ArchiveFileTypeIrda] = "Infrared",
};
static const char* known_ext[] = {
[ArchiveFileTypeIButton] = ".ibtn",
[ArchiveFileTypeNFC] = ".nfc",
[ArchiveFileTypeSubGhz] = ".sub",
[ArchiveFileTypeLFRFID] = ".rfid",
[ArchiveFileTypeIrda] = ".ir",
};
static const char* tab_default_paths[] = {
[ArchiveTabFavorites] = "/any/favorites",
[ArchiveTabIButton] = "/any/ibutton",
@ -53,23 +36,6 @@ static const char* tab_default_paths[] = {
[ArchiveTabBrowser] = "/any",
};
static inline const char* get_tab_ext(ArchiveTabEnum tab) {
switch(tab) {
case ArchiveTabIButton:
return known_ext[ArchiveFileTypeIButton];
case ArchiveTabNFC:
return known_ext[ArchiveFileTypeNFC];
case ArchiveTabSubGhz:
return known_ext[ArchiveFileTypeSubGhz];
case ArchiveTabLFRFID:
return known_ext[ArchiveFileTypeLFRFID];
case ArchiveTabIrda:
return known_ext[ArchiveFileTypeIrda];
default:
return "*";
}
}
static inline const char* get_default_path(ArchiveFileTypeEnum type) {
switch(type) {
case ArchiveFileTypeIButton:
@ -104,35 +70,11 @@ typedef struct {
EventType type;
} AppEvent;
typedef enum {
FavoritesCheck,
FavoritesRead,
FavoritesDelete,
FavoritesRename,
} FavActionsEnum;
typedef struct {
ArchiveTabEnum tab_id;
string_t name;
string_t path;
char text_input_buffer[MAX_NAME_LEN];
uint8_t depth;
uint16_t last_idx[MAX_DEPTH];
bool menu;
} ArchiveBrowser;
struct ArchiveApp {
osMessageQueueId_t event_queue;
FuriThread* app_thread;
Loader* loader;
Gui* gui;
ViewDispatcher* view_dispatcher;
View* view_archive_main;
SceneManager* scene_manager;
ArchiveMainView* main_view;
TextInput* text_input;
Storage* api;
FileWorker* file_worker;
ArchiveBrowser browser;
char text_store[MAX_NAME_LEN];
};

View File

@ -1,168 +0,0 @@
#include "archive_views.h"
static const char* ArchiveTabNames[] = {
[ArchiveTabFavorites] = "Favorites",
[ArchiveTabIButton] = "iButton",
[ArchiveTabNFC] = "NFC",
[ArchiveTabSubGhz] = "Sub-GHz",
[ArchiveTabLFRFID] = "RFID LF",
[ArchiveTabIrda] = "Infrared",
[ArchiveTabBrowser] = "Browser"};
static const Icon* ArchiveItemIcons[] = {
[ArchiveFileTypeIButton] = &I_ibutt_10px,
[ArchiveFileTypeNFC] = &I_Nfc_10px,
[ArchiveFileTypeSubGhz] = &I_sub1_10px,
[ArchiveFileTypeLFRFID] = &I_125_10px,
[ArchiveFileTypeIrda] = &I_ir_10px,
[ArchiveFileTypeFolder] = &I_dir_10px,
[ArchiveFileTypeUnknown] = &I_unknown_10px,
};
static void render_item_menu(Canvas* canvas, ArchiveViewModel* model) {
canvas_set_color(canvas, ColorWhite);
canvas_draw_box(canvas, 71, 17, 57, 46);
canvas_set_color(canvas, ColorBlack);
elements_slightly_rounded_frame(canvas, 70, 16, 58, 48);
string_t menu[MENU_ITEMS];
string_init_set_str(menu[0], "Run in app");
string_init_set_str(menu[1], "Pin");
string_init_set_str(menu[2], "Rename");
string_init_set_str(menu[3], "Delete");
ArchiveFile_t* selected = files_array_get(model->files, model->idx);
if(!is_known_app(selected->type)) {
string_set_str(menu[0], "---");
string_set_str(menu[1], "---");
string_set_str(menu[2], "---");
} else if(model->tab_idx == 0 || selected->fav) {
string_set_str(menu[1], "Unpin");
}
for(size_t i = 0; i < MENU_ITEMS; i++) {
canvas_draw_str(canvas, 82, 27 + i * 11, string_get_cstr(menu[i]));
string_clear(menu[i]);
}
canvas_draw_icon(canvas, 74, 20 + model->menu_idx * 11, &I_ButtonRight_4x7);
}
void archive_trim_file_ext(char* name) {
size_t str_len = strlen(name);
char* end = name + str_len;
while(end > name && *end != '.' && *end != '\\' && *end != '/') {
--end;
}
if((end > name && *end == '.') && (*(end - 1) != '\\' && *(end - 1) != '/')) {
*end = '\0';
}
}
static void archive_draw_frame(Canvas* canvas, uint16_t idx, bool scrollbar) {
canvas_set_color(canvas, ColorBlack);
canvas_draw_box(canvas, 0, 15 + idx * FRAME_HEIGHT, scrollbar ? 122 : 127, FRAME_HEIGHT);
canvas_set_color(canvas, ColorWhite);
canvas_draw_dot(canvas, 0, 15 + idx * FRAME_HEIGHT);
canvas_draw_dot(canvas, 1, 15 + idx * FRAME_HEIGHT);
canvas_draw_dot(canvas, 0, (15 + idx * FRAME_HEIGHT) + 1);
canvas_draw_dot(canvas, 0, (15 + idx * FRAME_HEIGHT) + 11);
canvas_draw_dot(canvas, scrollbar ? 121 : 126, 15 + idx * FRAME_HEIGHT);
canvas_draw_dot(canvas, scrollbar ? 121 : 126, (15 + idx * FRAME_HEIGHT) + 11);
}
static void draw_list(Canvas* canvas, ArchiveViewModel* model) {
furi_assert(model);
size_t array_size = files_array_size(model->files);
bool scrollbar = array_size > 4;
for(size_t i = 0; i < MIN(array_size, MENU_ITEMS); ++i) {
string_t str_buff;
char cstr_buff[MAX_NAME_LEN];
size_t idx = CLAMP(i + model->list_offset, array_size, 0);
ArchiveFile_t* file = files_array_get(model->files, CLAMP(idx, array_size - 1, 0));
string_init_set(str_buff, file->name);
string_right(str_buff, string_search_rchar(str_buff, '/') + 1);
strlcpy(cstr_buff, string_get_cstr(str_buff), string_size(str_buff) + 1);
if(is_known_app(file->type)) archive_trim_file_ext(cstr_buff);
string_clean(str_buff);
string_set_str(str_buff, cstr_buff);
elements_string_fit_width(canvas, str_buff, scrollbar ? MAX_LEN_PX - 6 : MAX_LEN_PX);
if(model->idx == idx) {
archive_draw_frame(canvas, i, scrollbar);
} else {
canvas_set_color(canvas, ColorBlack);
}
canvas_draw_icon(canvas, 2, 16 + i * FRAME_HEIGHT, ArchiveItemIcons[file->type]);
canvas_draw_str(canvas, 15, 24 + i * FRAME_HEIGHT, string_get_cstr(str_buff));
string_clear(str_buff);
}
if(scrollbar) {
elements_scrollbar_pos(canvas, 126, 15, 49, model->idx, array_size);
}
if(model->menu) {
render_item_menu(canvas, model);
}
}
static void archive_render_status_bar(Canvas* canvas, ArchiveViewModel* model) {
furi_assert(model);
const char* tab_name = ArchiveTabNames[model->tab_idx];
canvas_draw_icon(canvas, 0, 0, &I_Background_128x11);
canvas_set_color(canvas, ColorWhite);
canvas_draw_box(canvas, 0, 0, 50, 13);
canvas_draw_box(canvas, 107, 0, 20, 13);
canvas_set_color(canvas, ColorBlack);
canvas_draw_frame(canvas, 1, 0, 50, 12);
canvas_draw_line(canvas, 0, 1, 0, 11);
canvas_draw_line(canvas, 1, 12, 49, 12);
canvas_draw_str_aligned(canvas, 26, 9, AlignCenter, AlignBottom, tab_name);
canvas_draw_frame(canvas, 108, 0, 20, 12);
canvas_draw_line(canvas, 107, 1, 107, 11);
canvas_draw_line(canvas, 108, 12, 126, 12);
if(model->tab_idx > 0) {
canvas_draw_icon(canvas, 112, 2, &I_ButtonLeft_4x7);
}
if(model->tab_idx < SIZEOF_ARRAY(ArchiveTabNames) - 1) {
canvas_draw_icon(canvas, 120, 2, &I_ButtonRight_4x7);
}
canvas_set_color(canvas, ColorWhite);
canvas_draw_dot(canvas, 50, 0);
canvas_draw_dot(canvas, 127, 0);
canvas_set_color(canvas, ColorBlack);
}
void archive_view_render(Canvas* canvas, void* model) {
ArchiveViewModel* m = model;
archive_render_status_bar(canvas, model);
if(files_array_size(m->files) > 0) {
draw_list(canvas, m);
} else {
canvas_draw_str_aligned(
canvas, GUI_DISPLAY_WIDTH / 2, 40, AlignCenter, AlignCenter, "Empty");
}
}

View File

@ -0,0 +1,171 @@
#include "archive_favorites.h"
#include "archive_files.h"
#include "../views/archive_main_view.h"
bool archive_favorites_read(void* context) {
furi_assert(context);
ArchiveMainView* archive_view = context;
FileWorker* file_worker = file_worker_alloc(true);
string_t buffer;
FileInfo file_info;
string_init(buffer);
bool result = file_worker_open(file_worker, ARCHIVE_FAV_PATH, FSAM_READ, FSOM_OPEN_ALWAYS);
if(result) {
while(1) {
if(!file_worker_read_until(file_worker, buffer, '\n')) {
break;
}
if(!string_size(buffer)) {
break;
}
archive_view_add_item(archive_view, &file_info, string_get_cstr(buffer));
string_clean(buffer);
}
}
string_clear(buffer);
file_worker_close(file_worker);
file_worker_free(file_worker);
return result;
}
bool archive_favorites_delete(const char* file_path, const char* name) {
furi_assert(file_path);
furi_assert(name);
FileWorker* file_worker = file_worker_alloc(true);
string_t path;
string_t buffer;
string_init(buffer);
string_init_printf(path, "%s/%s", file_path, name);
bool result = file_worker_open(file_worker, ARCHIVE_FAV_PATH, FSAM_READ, FSOM_OPEN_EXISTING);
if(result) {
while(1) {
if(!file_worker_read_until(file_worker, buffer, '\n')) {
break;
}
if(!string_size(buffer)) {
break;
}
if(string_search(buffer, path)) {
string_t temp;
string_init_printf(temp, "%s\r\n", string_get_cstr(buffer));
archive_file_append(ARCHIVE_FAV_TEMP_PATH, temp);
string_clear(temp);
}
}
}
string_clear(buffer);
string_clear(path);
file_worker_close(file_worker);
file_worker_remove(file_worker, ARCHIVE_FAV_PATH);
file_worker_rename(file_worker, ARCHIVE_FAV_TEMP_PATH, ARCHIVE_FAV_PATH);
file_worker_free(file_worker);
return result;
}
bool archive_is_favorite(const char* file_path, const char* name) {
furi_assert(file_path);
furi_assert(name);
FileWorker* file_worker = file_worker_alloc(true);
string_t path;
string_t buffer;
string_init(buffer);
bool found = false;
string_init_printf(path, "%s/%s", file_path, name);
bool result = file_worker_open(file_worker, ARCHIVE_FAV_PATH, FSAM_READ, FSOM_OPEN_ALWAYS);
if(result) {
while(1) {
if(!file_worker_read_until(file_worker, buffer, '\n')) {
break;
}
if(!string_size(buffer)) {
break;
}
if(!string_search(buffer, path)) {
found = true;
break;
}
}
}
string_clear(buffer);
string_clear(path);
file_worker_close(file_worker);
file_worker_free(file_worker);
return found;
}
bool archive_favorites_rename(const char* file_path, const char* src, const char* dst) {
furi_assert(file_path);
furi_assert(src);
furi_assert(dst);
FileWorker* file_worker = file_worker_alloc(true);
string_t path;
string_t buffer;
string_t temp;
string_init(buffer);
string_init(temp);
string_init(path);
string_printf(path, "%s/%s", file_path, src);
bool result = file_worker_open(file_worker, ARCHIVE_FAV_PATH, FSAM_READ, FSOM_OPEN_EXISTING);
if(result) {
while(1) {
if(!file_worker_read_until(file_worker, buffer, '\n')) {
break;
}
if(!string_size(buffer)) {
break;
}
string_printf(
temp, "%s\r\n", string_search(buffer, path) ? string_get_cstr(buffer) : dst);
archive_file_append(ARCHIVE_FAV_TEMP_PATH, temp);
string_clean(temp);
}
}
string_clear(temp);
string_clear(buffer);
string_clear(path);
file_worker_close(file_worker);
file_worker_remove(file_worker, ARCHIVE_FAV_PATH);
file_worker_rename(file_worker, ARCHIVE_FAV_TEMP_PATH, ARCHIVE_FAV_PATH);
file_worker_free(file_worker);
return result;
}
void archive_add_to_favorites(const char* file_path, const char* name) {
furi_assert(file_path);
furi_assert(name);
string_t buffer_src;
string_init_printf(buffer_src, "%s/%s\r\n", file_path, name);
archive_file_append(ARCHIVE_FAV_PATH, buffer_src);
string_clear(buffer_src);
}

View File

@ -0,0 +1,11 @@
#pragma once
#include "file-worker.h"
#define ARCHIVE_FAV_PATH "/any/favorites.txt"
#define ARCHIVE_FAV_TEMP_PATH "/any/favorites.tmp"
bool archive_favorites_read(void* context);
bool archive_favorites_delete(const char* file_path, const char* name);
bool archive_is_favorite(const char* file_path, const char* name);
bool archive_favorites_rename(const char* file_path, const char* src, const char* dst);
void archive_add_to_favorites(const char* file_path, const char* name);

View File

@ -0,0 +1,143 @@
#include "archive_files.h"
#include "archive_favorites.h"
#include "../archive_i.h"
bool filter_by_extension(FileInfo* file_info, const char* tab_ext, const char* name) {
furi_assert(file_info);
furi_assert(tab_ext);
furi_assert(name);
bool result = false;
if(strcmp(tab_ext, "*") == 0) {
result = true;
} else if(strstr(name, tab_ext) != NULL) {
result = true;
} else if(file_info->flags & FSF_DIRECTORY) {
result = true;
}
return result;
}
void archive_trim_file_ext(char* name) {
size_t str_len = strlen(name);
char* end = name + str_len;
while(end > name && *end != '.' && *end != '\\' && *end != '/') {
--end;
}
if((end > name && *end == '.') && (*(end - 1) != '\\' && *(end - 1) != '/')) {
*end = '\0';
}
}
void set_file_type(ArchiveFile_t* file, FileInfo* file_info) {
furi_assert(file);
furi_assert(file_info);
for(size_t i = 0; i < SIZEOF_ARRAY(known_ext); i++) {
if(string_search_str(file->name, known_ext[i], 0) != STRING_FAILURE) {
file->type = i;
return;
}
}
if(file_info->flags & FSF_DIRECTORY) {
file->type = ArchiveFileTypeFolder;
} else {
file->type = ArchiveFileTypeUnknown;
}
}
bool archive_get_filenames(void* context, uint8_t tab_id, const char* path) {
furi_assert(context);
ArchiveMainView* main_view = context;
archive_file_array_clean(main_view);
if(tab_id != ArchiveTabFavorites) {
archive_read_dir(main_view, path);
} else {
archive_favorites_read(main_view);
}
return true;
}
bool archive_read_dir(void* context, const char* path) {
furi_assert(context);
ArchiveMainView* main_view = context;
FileInfo file_info;
Storage* fs_api = furi_record_open("storage");
File* directory = storage_file_alloc(fs_api);
char name[MAX_NAME_LEN];
if(!storage_dir_open(directory, path)) {
storage_dir_close(directory);
storage_file_free(directory);
return false;
}
while(1) {
if(!storage_dir_read(directory, &file_info, name, MAX_NAME_LEN)) {
break;
}
uint16_t files_cnt = archive_file_array_size(main_view);
if(files_cnt > MAX_FILES) {
break;
} else if(storage_file_get_error(directory) == FSE_OK) {
archive_view_add_item(main_view, &file_info, name);
} else {
storage_dir_close(directory);
storage_file_free(directory);
return false;
}
}
storage_dir_close(directory);
storage_file_free(directory);
furi_record_close("storage");
return true;
}
void archive_file_append(const char* path, string_t string) {
furi_assert(path);
furi_assert(string);
FileWorker* file_worker = file_worker_alloc(false);
if(!file_worker_open(file_worker, path, FSAM_WRITE, FSOM_OPEN_APPEND)) {
FURI_LOG_E("Archive", "Append open error");
}
if(!file_worker_write(file_worker, string_get_cstr(string), string_size(string))) {
FURI_LOG_E("Archive", "Append write error");
}
file_worker_close(file_worker);
file_worker_free(file_worker);
}
void archive_delete_file(void* context, string_t path, string_t name) {
furi_assert(context);
furi_assert(path);
furi_assert(name);
ArchiveMainView* main_view = context;
FileWorker* file_worker = file_worker_alloc(false);
string_t full_path;
string_init(full_path);
string_printf(full_path, "%s/%s", string_get_cstr(path), string_get_cstr(name));
file_worker_remove(file_worker, string_get_cstr(full_path));
file_worker_free(file_worker);
string_clear(full_path);
if(archive_is_favorite(string_get_cstr(path), string_get_cstr(name))) {
archive_favorites_delete(string_get_cstr(path), string_get_cstr(name));
}
archive_file_array_remove_selected(main_view);
}

View File

@ -1,15 +1,7 @@
#pragma once
#include "file-worker.h"
#include <gui/gui_i.h>
#include <gui/canvas.h>
#include <gui/elements.h>
#include <furi.h>
#include <storage/storage.h>
#define MAX_LEN_PX 100
#define MAX_NAME_LEN 255
#define FRAME_HEIGHT 12
#define MENU_ITEMS 4
#define MAX_FILES 100 //temp
typedef enum {
ArchiveFileTypeIButton,
@ -22,17 +14,6 @@ typedef enum {
AppIdTotal,
} ArchiveFileTypeEnum;
typedef enum {
ArchiveTabFavorites,
ArchiveTabLFRFID,
ArchiveTabSubGhz,
ArchiveTabNFC,
ArchiveTabIButton,
ArchiveTabIrda,
ArchiveTabBrowser,
ArchiveTabTotal,
} ArchiveTabEnum;
typedef struct {
string_t name;
ArchiveFileTypeEnum type;
@ -66,18 +47,10 @@ ARRAY_DEF(
INIT_SET(API_6(ArchiveFile_t_init_set)),
CLEAR(API_2(ArchiveFile_t_clear))))
typedef struct {
uint8_t tab_idx;
uint8_t menu_idx;
uint16_t idx;
uint16_t list_offset;
files_array_t files;
bool menu;
} ArchiveViewModel;
void archive_view_render(Canvas* canvas, void* model);
bool filter_by_extension(FileInfo* file_info, const char* tab_ext, const char* name);
void set_file_type(ArchiveFile_t* file, FileInfo* file_info);
void archive_trim_file_ext(char* name);
static inline bool is_known_app(ArchiveFileTypeEnum type) {
return (type != ArchiveFileTypeFolder && type != ArchiveFileTypeUnknown);
}
bool archive_get_filenames(void* context, uint8_t tab_id, const char* path);
bool archive_read_dir(void* context, const char* path);
void archive_file_append(const char* path, string_t string);
void archive_delete_file(void* context, string_t path, string_t name);

View File

@ -0,0 +1,30 @@
#include "archive_scene.h"
// Generate scene on_enter handlers array
#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_enter,
void (*const archive_on_enter_handlers[])(void*) = {
#include "archive_scene_config.h"
};
#undef ADD_SCENE
// Generate scene on_event handlers array
#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_event,
bool (*const archive_on_event_handlers[])(void* context, SceneManagerEvent event) = {
#include "archive_scene_config.h"
};
#undef ADD_SCENE
// Generate scene on_exit handlers array
#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_exit,
void (*const archive_on_exit_handlers[])(void* context) = {
#include "archive_scene_config.h"
};
#undef ADD_SCENE
// Initialize scene handlers configuration structure
const SceneManagerHandlers archive_scene_handlers = {
.on_enter_handlers = archive_on_enter_handlers,
.on_event_handlers = archive_on_event_handlers,
.on_exit_handlers = archive_on_exit_handlers,
.scene_num = ArchiveAppSceneNum,
};

View File

@ -0,0 +1,29 @@
#pragma once
#include <gui/scene_manager.h>
// Generate scene id and total number
#define ADD_SCENE(prefix, name, id) ArchiveAppScene##id,
typedef enum {
#include "archive_scene_config.h"
ArchiveAppSceneNum,
} ArchiveAppScene;
#undef ADD_SCENE
extern const SceneManagerHandlers archive_scene_handlers;
// Generate scene on_enter handlers declaration
#define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_enter(void*);
#include "archive_scene_config.h"
#undef ADD_SCENE
// Generate scene on_event handlers declaration
#define ADD_SCENE(prefix, name, id) \
bool prefix##_scene_##name##_on_event(void* context, SceneManagerEvent event);
#include "archive_scene_config.h"
#undef ADD_SCENE
// Generate scene on_exit handlers declaration
#define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_exit(void* context);
#include "archive_scene_config.h"
#undef ADD_SCENE

View File

@ -0,0 +1,42 @@
#include "../archive_i.h"
#include "../views/archive_main_view.h"
void archive_scene_browser_callback(ArchiveBrowserEvent event, void* context) {
ArchiveApp* archive = (ArchiveApp*)context;
view_dispatcher_send_custom_event(archive->view_dispatcher, event);
}
const void archive_scene_browser_on_enter(void* context) {
ArchiveApp* archive = (ArchiveApp*)context;
ArchiveMainView* main_view = archive->main_view;
archive_browser_set_callback(main_view, archive_scene_browser_callback, archive);
archive_browser_update(main_view);
view_dispatcher_switch_to_view(archive->view_dispatcher, ArchiveViewBrowser);
}
const bool archive_scene_browser_on_event(void* context, SceneManagerEvent event) {
ArchiveApp* archive = (ArchiveApp*)context;
bool consumed;
if(event.type == SceneManagerEventTypeCustom) {
switch(event.event) {
case ArchiveBrowserEventRename:
scene_manager_next_scene(archive->scene_manager, ArchiveAppSceneRename);
consumed = true;
break;
case ArchiveBrowserEventExit:
view_dispatcher_stop(archive->view_dispatcher);
consumed = true;
break;
default:
break;
}
}
return consumed;
}
const void archive_scene_browser_on_exit(void* context) {
// ArchiveApp* archive = (ArchiveApp*)context;
}

View File

@ -0,0 +1,2 @@
ADD_SCENE(archive, browser, Browser)
ADD_SCENE(archive, rename, Rename)

View File

@ -0,0 +1,80 @@
#include "../archive_i.h"
#include "../helpers/archive_favorites.h"
#include "../helpers/archive_files.h"
#define SCENE_RENAME_CUSTOM_EVENT (0UL)
void archive_scene_rename_text_input_callback(void* context) {
ArchiveApp* archive = (ArchiveApp*)context;
view_dispatcher_send_custom_event(archive->view_dispatcher, SCENE_RENAME_CUSTOM_EVENT);
}
const void archive_scene_rename_on_enter(void* context) {
ArchiveApp* archive = (ArchiveApp*)context;
TextInput* text_input = archive->text_input;
ArchiveFile_t* current = archive_get_current_file(archive->main_view);
strlcpy(archive->text_store, string_get_cstr(current->name), MAX_NAME_LEN);
archive_trim_file_ext(archive->text_store);
text_input_set_header_text(text_input, "Rename:");
text_input_set_result_callback(
text_input,
archive_scene_rename_text_input_callback,
archive,
archive->text_store,
MAX_NAME_LEN,
false);
view_dispatcher_switch_to_view(archive->view_dispatcher, ArchiveViewTextInput);
}
const bool archive_scene_rename_on_event(void* context, SceneManagerEvent event) {
ArchiveApp* archive = (ArchiveApp*)context;
bool consumed = false;
if(event.type == SceneManagerEventTypeCustom) {
if(event.event == SCENE_RENAME_CUSTOM_EVENT) {
Storage* fs_api = furi_record_open("storage");
string_t buffer_src;
string_t buffer_dst;
const char* path = archive_get_path(archive->main_view);
const char* name = archive_get_name(archive->main_view);
string_init_printf(buffer_src, "%s/%s", path, name);
string_init_printf(buffer_dst, "%s/%s", path, archive->text_store);
archive_set_name(archive->main_view, archive->text_store);
// append extension
ArchiveFile_t* file = archive_get_current_file(archive->main_view);
string_cat(buffer_dst, known_ext[file->type]);
storage_common_rename(
fs_api, string_get_cstr(buffer_src), string_get_cstr(buffer_dst));
furi_record_close("storage");
if(file->fav) {
archive_favorites_rename(path, name, string_get_cstr(buffer_dst));
}
string_clear(buffer_src);
string_clear(buffer_dst);
scene_manager_next_scene(archive->scene_manager, ArchiveAppSceneBrowser);
consumed = true;
}
}
return consumed;
}
const void archive_scene_rename_on_exit(void* context) {
ArchiveApp* archive = (ArchiveApp*)context;
// Clear view
text_input_set_header_text(archive->text_input, NULL);
text_input_set_result_callback(archive->text_input, NULL, NULL, NULL, 0, false);
}

View File

@ -0,0 +1,641 @@
#include <furi.h>
#include "../archive_i.h"
#include "archive_main_view.h"
static const char* flipper_app_name[] = {
[ArchiveFileTypeIButton] = "iButton",
[ArchiveFileTypeNFC] = "NFC",
[ArchiveFileTypeSubGhz] = "Sub-GHz",
[ArchiveFileTypeLFRFID] = "125 kHz RFID",
[ArchiveFileTypeIrda] = "Infrared",
};
static const char* ArchiveTabNames[] = {
[ArchiveTabFavorites] = "Favorites",
[ArchiveTabIButton] = "iButton",
[ArchiveTabNFC] = "NFC",
[ArchiveTabSubGhz] = "Sub-GHz",
[ArchiveTabLFRFID] = "RFID LF",
[ArchiveTabIrda] = "Infrared",
[ArchiveTabBrowser] = "Browser"};
static const Icon* ArchiveItemIcons[] = {
[ArchiveFileTypeIButton] = &I_ibutt_10px,
[ArchiveFileTypeNFC] = &I_Nfc_10px,
[ArchiveFileTypeSubGhz] = &I_sub1_10px,
[ArchiveFileTypeLFRFID] = &I_125_10px,
[ArchiveFileTypeIrda] = &I_ir_10px,
[ArchiveFileTypeFolder] = &I_dir_10px,
[ArchiveFileTypeUnknown] = &I_unknown_10px,
};
void archive_browser_set_callback(
ArchiveMainView* main_view,
ArchiveMainViewCallback callback,
void* context) {
furi_assert(main_view);
furi_assert(callback);
main_view->callback = callback;
main_view->context = context;
}
void update_offset(ArchiveMainView* main_view) {
furi_assert(main_view);
with_view_model(
main_view->view, (ArchiveMainViewModel * model) {
size_t array_size = files_array_size(model->files);
uint16_t bounds = array_size > 3 ? 2 : array_size;
if(array_size > 3 && model->idx >= array_size - 1) {
model->list_offset = model->idx - 3;
} else if(model->list_offset < model->idx - bounds) {
model->list_offset = CLAMP(model->idx - 2, array_size - bounds, 0);
} else if(model->list_offset > model->idx - bounds) {
model->list_offset = CLAMP(model->idx - 1, array_size - bounds, 0);
}
return true;
});
}
size_t archive_file_array_size(ArchiveMainView* main_view) {
uint16_t size = 0;
with_view_model(
main_view->view, (ArchiveMainViewModel * model) {
size = files_array_size(model->files);
return true;
});
return size;
}
void archive_file_array_remove_selected(ArchiveMainView* main_view) {
with_view_model(
main_view->view, (ArchiveMainViewModel * model) {
files_array_remove_v(model->files, model->idx, model->idx + 1);
model->idx = CLAMP(model->idx, files_array_size(model->files) - 1, 0);
return true;
});
update_offset(main_view);
}
void archive_file_array_clean(ArchiveMainView* main_view) {
with_view_model(
main_view->view, (ArchiveMainViewModel * model) {
files_array_clean(model->files);
return true;
});
}
ArchiveFile_t* archive_get_current_file(ArchiveMainView* main_view) {
ArchiveFile_t* selected;
with_view_model(
main_view->view, (ArchiveMainViewModel * model) {
selected = files_array_size(model->files) > 0 ?
files_array_get(model->files, model->idx) :
NULL;
return true;
});
return selected;
}
ArchiveTabEnum archive_get_tab(ArchiveMainView* main_view) {
ArchiveTabEnum tab_id;
with_view_model(
main_view->view, (ArchiveMainViewModel * model) {
tab_id = model->tab_idx;
return true;
});
return tab_id;
}
void archive_set_tab(ArchiveMainView* main_view, ArchiveTabEnum tab) {
with_view_model(
main_view->view, (ArchiveMainViewModel * model) {
model->tab_idx = tab;
return true;
});
}
uint8_t archive_get_depth(ArchiveMainView* main_view) {
uint8_t depth;
with_view_model(
main_view->view, (ArchiveMainViewModel * model) {
depth = model->depth;
return true;
});
return depth;
}
const char* archive_get_path(ArchiveMainView* main_view) {
return string_get_cstr(main_view->path);
}
const char* archive_get_name(ArchiveMainView* main_view) {
ArchiveFile_t* selected = archive_get_current_file(main_view);
return string_get_cstr(selected->name);
}
void archive_set_name(ArchiveMainView* main_view, const char* name) {
furi_assert(main_view);
furi_assert(name);
string_set(main_view->name, name);
}
void archive_browser_update(ArchiveMainView* main_view) {
furi_assert(main_view);
archive_get_filenames(main_view, archive_get_tab(main_view), string_get_cstr(main_view->path));
with_view_model(
main_view->view, (ArchiveMainViewModel * model) {
uint16_t idx = 0;
while(idx < files_array_size(model->files)) {
ArchiveFile_t* current = files_array_get(model->files, idx);
if(!string_search(current->name, string_get_cstr(main_view->name))) {
model->idx = idx;
break;
}
++idx;
}
return true;
});
update_offset(main_view);
}
void archive_view_add_item(ArchiveMainView* main_view, FileInfo* file_info, const char* name) {
furi_assert(main_view);
furi_assert(file_info);
furi_assert(name);
ArchiveFile_t item;
if(filter_by_extension(file_info, get_tab_ext(archive_get_tab(main_view)), name)) {
ArchiveFile_t_init(&item);
string_init_set_str(item.name, name);
set_file_type(&item, file_info);
with_view_model(
main_view->view, (ArchiveMainViewModel * model) {
files_array_push_back(model->files, item);
return true;
});
ArchiveFile_t_clear(&item);
}
}
static void render_item_menu(Canvas* canvas, ArchiveMainViewModel* model) {
canvas_set_color(canvas, ColorWhite);
canvas_draw_box(canvas, 71, 17, 57, 46);
canvas_set_color(canvas, ColorBlack);
elements_slightly_rounded_frame(canvas, 70, 16, 58, 48);
string_t menu[MENU_ITEMS];
string_init_set_str(menu[0], "Run in app");
string_init_set_str(menu[1], "Pin");
string_init_set_str(menu[2], "Rename");
string_init_set_str(menu[3], "Delete");
ArchiveFile_t* selected = files_array_get(model->files, model->idx);
if(!is_known_app(selected->type)) {
string_set_str(menu[0], "---");
string_set_str(menu[1], "---");
string_set_str(menu[2], "---");
} else if(selected->fav) {
string_set_str(menu[1], "Unpin");
}
for(size_t i = 0; i < MENU_ITEMS; i++) {
canvas_draw_str(canvas, 82, 27 + i * 11, string_get_cstr(menu[i]));
string_clear(menu[i]);
}
canvas_draw_icon(canvas, 74, 20 + model->menu_idx * 11, &I_ButtonRight_4x7);
}
static void archive_draw_frame(Canvas* canvas, uint16_t idx, bool scrollbar) {
canvas_set_color(canvas, ColorBlack);
canvas_draw_box(canvas, 0, 15 + idx * FRAME_HEIGHT, scrollbar ? 122 : 127, FRAME_HEIGHT);
canvas_set_color(canvas, ColorWhite);
canvas_draw_dot(canvas, 0, 15 + idx * FRAME_HEIGHT);
canvas_draw_dot(canvas, 1, 15 + idx * FRAME_HEIGHT);
canvas_draw_dot(canvas, 0, (15 + idx * FRAME_HEIGHT) + 1);
canvas_draw_dot(canvas, 0, (15 + idx * FRAME_HEIGHT) + 11);
canvas_draw_dot(canvas, scrollbar ? 121 : 126, 15 + idx * FRAME_HEIGHT);
canvas_draw_dot(canvas, scrollbar ? 121 : 126, (15 + idx * FRAME_HEIGHT) + 11);
}
static void draw_list(Canvas* canvas, ArchiveMainViewModel* model) {
furi_assert(model);
size_t array_size = files_array_size(model->files);
bool scrollbar = array_size > 4;
for(size_t i = 0; i < MIN(array_size, MENU_ITEMS); ++i) {
string_t str_buff;
char cstr_buff[MAX_NAME_LEN];
size_t idx = CLAMP(i + model->list_offset, array_size, 0);
ArchiveFile_t* file = files_array_get(model->files, CLAMP(idx, array_size - 1, 0));
string_init_set(str_buff, file->name);
string_right(str_buff, string_search_rchar(str_buff, '/') + 1);
strlcpy(cstr_buff, string_get_cstr(str_buff), string_size(str_buff) + 1);
if(is_known_app(file->type)) archive_trim_file_ext(cstr_buff);
string_clean(str_buff);
string_set_str(str_buff, cstr_buff);
elements_string_fit_width(canvas, str_buff, scrollbar ? MAX_LEN_PX - 6 : MAX_LEN_PX);
if(model->idx == idx) {
archive_draw_frame(canvas, i, scrollbar);
} else {
canvas_set_color(canvas, ColorBlack);
}
canvas_draw_icon(canvas, 2, 16 + i * FRAME_HEIGHT, ArchiveItemIcons[file->type]);
canvas_draw_str(canvas, 15, 24 + i * FRAME_HEIGHT, string_get_cstr(str_buff));
string_clear(str_buff);
}
if(scrollbar) {
elements_scrollbar_pos(canvas, 126, 15, 49, model->idx, array_size);
}
if(model->action == BrowserActionItemMenu) {
render_item_menu(canvas, model);
}
}
static void archive_render_status_bar(Canvas* canvas, ArchiveMainViewModel* model) {
furi_assert(model);
const char* tab_name = ArchiveTabNames[model->tab_idx];
canvas_draw_icon(canvas, 0, 0, &I_Background_128x11);
canvas_set_color(canvas, ColorWhite);
canvas_draw_box(canvas, 0, 0, 50, 13);
canvas_draw_box(canvas, 107, 0, 20, 13);
canvas_set_color(canvas, ColorBlack);
canvas_draw_frame(canvas, 1, 0, 50, 12);
canvas_draw_line(canvas, 0, 1, 0, 11);
canvas_draw_line(canvas, 1, 12, 49, 12);
canvas_draw_str_aligned(canvas, 26, 9, AlignCenter, AlignBottom, tab_name);
canvas_draw_frame(canvas, 108, 0, 20, 12);
canvas_draw_line(canvas, 107, 1, 107, 11);
canvas_draw_line(canvas, 108, 12, 126, 12);
if(model->tab_idx > 0) {
canvas_draw_icon(canvas, 112, 2, &I_ButtonLeft_4x7);
}
if(model->tab_idx < SIZEOF_ARRAY(ArchiveTabNames) - 1) {
canvas_draw_icon(canvas, 120, 2, &I_ButtonRight_4x7);
}
canvas_set_color(canvas, ColorWhite);
canvas_draw_dot(canvas, 50, 0);
canvas_draw_dot(canvas, 127, 0);
canvas_set_color(canvas, ColorBlack);
}
void archive_view_render(Canvas* canvas, void* model) {
ArchiveMainViewModel* m = model;
archive_render_status_bar(canvas, model);
if(files_array_size(m->files) > 0) {
draw_list(canvas, m);
} else {
canvas_draw_str_aligned(
canvas, GUI_DISPLAY_WIDTH / 2, 40, AlignCenter, AlignCenter, "Empty");
}
}
View* archive_main_get_view(ArchiveMainView* main_view) {
furi_assert(main_view);
return main_view->view;
}
static void archive_show_file_menu(ArchiveMainView* main_view) {
furi_assert(main_view);
with_view_model(
main_view->view, (ArchiveMainViewModel * model) {
ArchiveFile_t* selected;
selected = files_array_get(model->files, model->idx);
model->action = BrowserActionItemMenu;
model->menu_idx = 0;
selected->fav = is_known_app(selected->type) ? archive_is_favorite(
string_get_cstr(main_view->path),
string_get_cstr(selected->name)) :
false;
return true;
});
}
static void archive_close_file_menu(ArchiveMainView* main_view) {
furi_assert(main_view);
with_view_model(
main_view->view, (ArchiveMainViewModel * model) {
model->action = BrowserActionBrowse;
model->menu_idx = 0;
return true;
});
}
static void archive_run_in_app(
ArchiveMainView* main_view,
ArchiveFile_t* selected,
bool full_path_provided) {
Loader* loader = furi_record_open("loader");
string_t full_path;
if(!full_path_provided) {
string_init_printf(
full_path, "%s/%s", string_get_cstr(main_view->path), string_get_cstr(selected->name));
} else {
string_init_set(full_path, selected->name);
}
loader_start(loader, flipper_app_name[selected->type], string_get_cstr(full_path));
string_clear(full_path);
furi_record_close("loader");
}
static void archive_file_menu_callback(ArchiveMainView* main_view) {
furi_assert(main_view);
ArchiveFile_t* selected = archive_get_current_file(main_view);
const char* path = archive_get_path(main_view);
const char* name = archive_get_name(main_view);
uint8_t idx;
with_view_model(
main_view->view, (ArchiveMainViewModel * model) {
idx = model->menu_idx;
return true;
});
switch(idx) {
case 0:
if(is_known_app(selected->type)) {
archive_run_in_app(main_view, selected, false);
}
break;
case 1:
if(is_known_app(selected->type)) {
if(!archive_is_favorite(path, name)) {
archive_set_name(main_view, string_get_cstr(selected->name));
archive_add_to_favorites(path, name);
} else {
// delete from favorites
archive_favorites_delete(path, name);
}
archive_close_file_menu(main_view);
}
break;
case 2:
// open rename view
if(is_known_app(selected->type)) {
main_view->callback(ArchiveBrowserEventRename, main_view->context);
}
break;
case 3:
// confirmation?
archive_delete_file(main_view, main_view->path, selected->name);
archive_close_file_menu(main_view);
break;
default:
archive_close_file_menu(main_view);
break;
}
selected = NULL;
}
static void archive_switch_dir(ArchiveMainView* main_view, const char* path) {
furi_assert(main_view);
furi_assert(path);
string_set(main_view->path, path);
archive_get_filenames(main_view, archive_get_tab(main_view), string_get_cstr(main_view->path));
update_offset(main_view);
}
void archive_switch_tab(ArchiveMainView* main_view) {
furi_assert(main_view);
with_view_model(
main_view->view, (ArchiveMainViewModel * model) {
model->idx = 0;
model->depth = 0;
return true;
});
archive_switch_dir(main_view, tab_default_paths[archive_get_tab(main_view)]);
}
static void archive_enter_dir(ArchiveMainView* main_view, string_t name) {
furi_assert(main_view);
furi_assert(name);
// update last index
with_view_model(
main_view->view, (ArchiveMainViewModel * model) {
model->last_idx[model->depth] =
CLAMP(model->idx, files_array_size(model->files) - 1, 0);
model->idx = 0;
model->depth = CLAMP(model->depth + 1, MAX_DEPTH, 0);
return true;
});
string_cat(main_view->path, "/");
string_cat(main_view->path, main_view->name);
archive_switch_dir(main_view, string_get_cstr(main_view->path));
}
static void archive_leave_dir(ArchiveMainView* main_view) {
furi_assert(main_view);
char* last_char_ptr = strrchr(string_get_cstr(main_view->path), '/');
if(last_char_ptr) {
size_t pos = last_char_ptr - string_get_cstr(main_view->path);
string_left(main_view->path, pos);
}
with_view_model(
main_view->view, (ArchiveMainViewModel * model) {
model->depth = CLAMP(model->depth - 1, MAX_DEPTH, 0);
model->idx = model->last_idx[model->depth];
return true;
});
archive_switch_dir(main_view, string_get_cstr(main_view->path));
}
bool archive_view_input(InputEvent* event, void* context) {
furi_assert(event);
furi_assert(context);
ArchiveMainView* main_view = context;
BrowserActionEnum action;
with_view_model(
main_view->view, (ArchiveMainViewModel * model) {
action = model->action;
return true;
});
switch(action) {
case BrowserActionItemMenu:
if(event->type == InputTypeShort) {
if(event->key == InputKeyUp || event->key == InputKeyDown) {
with_view_model(
main_view->view, (ArchiveMainViewModel * model) {
if(event->key == InputKeyUp) {
model->menu_idx = ((model->menu_idx - 1) + MENU_ITEMS) % MENU_ITEMS;
} else if(event->key == InputKeyDown) {
model->menu_idx = (model->menu_idx + 1) % MENU_ITEMS;
}
return true;
});
}
if(event->key == InputKeyOk) {
archive_file_menu_callback(main_view);
} else if(event->key == InputKeyBack) {
archive_close_file_menu(main_view);
}
}
break;
case BrowserActionBrowse:
if(event->type == InputTypeShort) {
if(event->key == InputKeyLeft) {
ArchiveTabEnum tab = archive_get_tab(main_view);
if(tab) {
archive_set_tab(main_view, CLAMP(tab - 1, ArchiveTabTotal, 0));
archive_switch_tab(main_view);
return true;
}
} else if(event->key == InputKeyRight) {
ArchiveTabEnum tab = archive_get_tab(main_view);
if(tab < ArchiveTabTotal - 1) {
archive_set_tab(main_view, CLAMP(tab + 1, ArchiveTabTotal - 1, 0));
archive_switch_tab(main_view);
return true;
}
} else if(event->key == InputKeyBack) {
if(!archive_get_depth(main_view)) {
main_view->callback(ArchiveBrowserEventExit, main_view->context);
} else {
archive_leave_dir(main_view);
}
return true;
}
}
if(event->key == InputKeyUp || event->key == InputKeyDown) {
with_view_model(
main_view->view, (ArchiveMainViewModel * model) {
uint16_t num_elements = (uint16_t)files_array_size(model->files);
if((event->type == InputTypeShort || event->type == InputTypeRepeat)) {
if(event->key == InputKeyUp) {
model->idx = ((model->idx - 1) + num_elements) % num_elements;
} else if(event->key == InputKeyDown) {
model->idx = (model->idx + 1) % num_elements;
}
}
return true;
});
update_offset(main_view);
}
if(event->key == InputKeyOk) {
ArchiveFile_t* selected = archive_get_current_file(main_view);
if(selected) {
archive_set_name(main_view, string_get_cstr(selected->name));
if(selected->type == ArchiveFileTypeFolder) {
if(event->type == InputTypeShort) {
archive_enter_dir(main_view, main_view->name);
} else if(event->type == InputTypeLong) {
archive_show_file_menu(main_view);
}
} else {
if(event->type == InputTypeShort) {
if(archive_get_tab(main_view) == ArchiveTabFavorites) {
if(is_known_app(selected->type)) {
archive_run_in_app(main_view, selected, true);
}
} else {
archive_show_file_menu(main_view);
}
}
}
}
}
break;
default:
break;
}
return true;
}
ArchiveMainView* main_view_alloc() {
ArchiveMainView* main_view = furi_alloc(sizeof(ArchiveMainView));
main_view->view = view_alloc();
view_allocate_model(main_view->view, ViewModelTypeLocking, sizeof(ArchiveMainViewModel));
view_set_context(main_view->view, main_view);
view_set_draw_callback(main_view->view, (ViewDrawCallback)archive_view_render);
view_set_input_callback(main_view->view, archive_view_input);
string_init(main_view->name);
string_init(main_view->path);
with_view_model(
main_view->view, (ArchiveMainViewModel * model) {
files_array_init(model->files);
return true;
});
return main_view;
}
void main_view_free(ArchiveMainView* main_view) {
furi_assert(main_view);
with_view_model(
main_view->view, (ArchiveMainViewModel * model) {
files_array_clear(model->files);
return false;
});
string_clear(main_view->name);
string_clear(main_view->path);
view_free(main_view->view);
free(main_view);
}

View File

@ -0,0 +1,117 @@
#pragma once
#include <gui/gui_i.h>
#include <gui/view.h>
#include <gui/canvas.h>
#include <gui/elements.h>
#include <furi.h>
#include <storage/storage.h>
#include "../helpers/archive_files.h"
#include "../helpers/archive_favorites.h"
#define MAX_LEN_PX 110
#define MAX_NAME_LEN 255
#define FRAME_HEIGHT 12
#define MENU_ITEMS 4
#define MAX_DEPTH 32
typedef enum {
ArchiveTabFavorites,
ArchiveTabLFRFID,
ArchiveTabSubGhz,
ArchiveTabNFC,
ArchiveTabIButton,
ArchiveTabIrda,
ArchiveTabBrowser,
ArchiveTabTotal,
} ArchiveTabEnum;
static const char* known_ext[] = {
[ArchiveFileTypeIButton] = ".ibtn",
[ArchiveFileTypeNFC] = ".nfc",
[ArchiveFileTypeSubGhz] = ".sub",
[ArchiveFileTypeLFRFID] = ".rfid",
[ArchiveFileTypeIrda] = ".ir",
};
static inline const char* get_tab_ext(ArchiveTabEnum tab) {
switch(tab) {
case ArchiveTabIButton:
return known_ext[ArchiveFileTypeIButton];
case ArchiveTabNFC:
return known_ext[ArchiveFileTypeNFC];
case ArchiveTabSubGhz:
return known_ext[ArchiveFileTypeSubGhz];
case ArchiveTabLFRFID:
return known_ext[ArchiveFileTypeLFRFID];
case ArchiveTabIrda:
return known_ext[ArchiveFileTypeIrda];
default:
return "*";
}
}
typedef enum {
ArchiveBrowserEventRename,
ArchiveBrowserEventExit,
ArchiveBrowserEventLeaveDir,
} ArchiveBrowserEvent;
typedef struct ArchiveMainView ArchiveMainView;
typedef void (*ArchiveMainViewCallback)(ArchiveBrowserEvent event, void* context);
typedef enum {
BrowserActionBrowse,
BrowserActionItemMenu,
BrowserActionTotal,
} BrowserActionEnum;
struct ArchiveMainView {
View* view;
ArchiveMainViewCallback callback;
void* context;
string_t name;
string_t path;
};
typedef struct {
ArchiveTabEnum tab_idx;
BrowserActionEnum action;
files_array_t files;
uint8_t depth;
uint8_t menu_idx;
uint16_t idx;
uint16_t last_idx[MAX_DEPTH];
uint16_t list_offset;
} ArchiveMainViewModel;
void archive_browser_set_callback(
ArchiveMainView* main_view,
ArchiveMainViewCallback callback,
void* context);
View* archive_main_get_view(ArchiveMainView* main_view);
ArchiveMainView* main_view_alloc();
void main_view_free(ArchiveMainView* main_view);
void archive_file_array_remove_selected(ArchiveMainView* main_view);
void archive_file_array_clean(ArchiveMainView* main_view);
void archive_view_add_item(ArchiveMainView* main_view, FileInfo* file_info, const char* name);
void archive_browser_update(ArchiveMainView* main_view);
size_t archive_file_array_size(ArchiveMainView* main_view);
ArchiveFile_t* archive_get_current_file(ArchiveMainView* main_view);
const char* archive_get_path(ArchiveMainView* main_view);
const char* archive_get_name(ArchiveMainView* main_view);
void archive_set_name(ArchiveMainView* main_view, const char* name);
static inline bool is_known_app(ArchiveFileTypeEnum type) {
return (type != ArchiveFileTypeFolder && type != ArchiveFileTypeUnknown);
}

25
applications/bt/bt_service/bt.c Normal file → Executable file
View File

@ -1,4 +1,5 @@
#include "bt_i.h"
#include "battery_service.h"
#define BT_SERVICE_TAG "BT"
@ -21,6 +22,17 @@ static ViewPort* bt_statusbar_view_port_alloc() {
return statusbar_view_port;
}
static void bt_pin_code_show_event_handler(Bt* bt, uint32_t pin) {
furi_assert(bt);
string_t pin_str;
string_init_printf(pin_str, "%06d", pin);
dialog_message_set_text(
bt->dialog_message, string_get_cstr(pin_str), 64, 32, AlignCenter, AlignCenter);
dialog_message_set_buttons(bt->dialog_message, "Back", NULL, NULL);
dialog_message_show(bt->dialogs, bt->dialog_message);
string_clear(pin_str);
}
Bt* bt_alloc() {
Bt* bt = furi_alloc(sizeof(Bt));
// Load settings
@ -40,13 +52,16 @@ Bt* bt_alloc() {
bt->gui = furi_record_open("gui");
gui_add_view_port(bt->gui, bt->statusbar_view_port, GuiLayerStatusBarLeft);
// Dialogs
bt->dialogs = furi_record_open("dialogs");
bt->dialog_message = dialog_message_alloc();
return bt;
}
int32_t bt_srv() {
Bt* bt = bt_alloc();
furi_record_create("bt", bt);
furi_hal_bt_init();
if(!furi_hal_bt_wait_startup()) {
FURI_LOG_E(BT_SERVICE_TAG, "Core2 startup failed");
@ -68,6 +83,14 @@ int32_t bt_srv() {
if(message.type == BtMessageTypeUpdateStatusbar) {
// Update statusbar
view_port_enabled_set(bt->statusbar_view_port, furi_hal_bt_is_alive());
} else if(message.type == BtMessageTypeUpdateBatteryLevel) {
// Update battery level
if(furi_hal_bt_is_alive()) {
battery_svc_update_level(message.data.battery_level);
}
} else if(message.type == BtMessageTypePinCodeShow) {
// Display PIN code
bt_pin_code_show_event_handler(bt, message.data.pin_code);
}
}
return 0;

View File

@ -1,3 +1,18 @@
#pragma once
#include <stdint.h>
#include <stdbool.h>
#ifdef __cplusplus
extern "C" {
#endif
typedef struct Bt Bt;
void bt_update_battery_level(Bt* bt, uint8_t battery_level);
bool bt_pin_code_show(Bt* bt, uint32_t pin_code);
#ifdef __cplusplus
}
#endif

View File

@ -0,0 +1,15 @@
#include "bt.h"
#include "bt_i.h"
void bt_update_battery_level(Bt* bt, uint8_t battery_level) {
furi_assert(bt);
BtMessage message = {
.type = BtMessageTypeUpdateBatteryLevel, .data.battery_level = battery_level};
furi_check(osMessageQueuePut(bt->message_queue, &message, 0, osWaitForever) == osOK);
}
bool bt_pin_code_show(Bt* bt, uint32_t pin_code) {
furi_assert(bt);
BtMessage message = {.type = BtMessageTypePinCodeShow, .data.pin_code = pin_code};
return osMessageQueuePut(bt->message_queue, &message, 0, 0) == osOK;
}

View File

@ -9,15 +9,24 @@
#include <gui/view_port.h>
#include <gui/view.h>
#include <applications/dialogs/dialogs.h>
#include "../bt_settings.h"
typedef enum {
BtMessageTypeUpdateStatusbar,
BtMessageTypeUpdateBatteryLevel,
BtMessageTypePinCodeShow,
} BtMessageType;
typedef union {
uint32_t pin_code;
uint8_t battery_level;
} BtMessageData;
typedef struct {
BtMessageType type;
void* param;
BtMessageData data;
} BtMessage;
struct Bt {
@ -26,4 +35,6 @@ struct Bt {
osTimerId_t update_status_timer;
Gui* gui;
ViewPort* statusbar_view_port;
DialogsApp* dialogs;
DialogMessage* dialog_message;
};

View File

@ -68,10 +68,10 @@ static void variable_item_list_draw_callback(Canvas* canvas, void* _model) {
canvas_draw_str(canvas, 73, item_text_y, "<");
}
canvas_draw_str(canvas, 84, item_text_y, string_get_cstr(item->current_value_text));
canvas_draw_str(canvas, 80, item_text_y, string_get_cstr(item->current_value_text));
if(item->current_value_index < (item->values_count - 1)) {
canvas_draw_str(canvas, 113, item_text_y, ">");
canvas_draw_str(canvas, 115, item_text_y, ">");
}
}
@ -235,6 +235,21 @@ void variable_item_list_free(VariableItemList* variable_item_list) {
free(variable_item_list);
}
void variable_item_list_clean(VariableItemList* variable_item_list) {
furi_assert(variable_item_list);
with_view_model(
variable_item_list->view, (VariableItemListModel * model) {
VariableItemArray_it_t it;
for(VariableItemArray_it(it, model->items); !VariableItemArray_end_p(it);
VariableItemArray_next(it)) {
string_clean(VariableItemArray_ref(it)->current_value_text);
}
VariableItemArray_clean(model->items);
return false;
});
}
View* variable_item_list_get_view(VariableItemList* variable_item_list) {
furi_assert(variable_item_list);
return variable_item_list->view;

View File

@ -18,6 +18,9 @@ VariableItemList* variable_item_list_alloc();
* @param variable_item_list VariableItemList instance
*/
void variable_item_list_free(VariableItemList* variable_item_list);
void variable_item_list_clean(VariableItemList* variable_item_list);
View* variable_item_list_get_view(VariableItemList* variable_item_list);
/** Add item to VariableItemList

View File

@ -118,6 +118,20 @@ static void widget_add_element(Widget* widget, WidgetElement* element) {
});
}
void widget_add_string_multi_element(
Widget* widget,
uint8_t x,
uint8_t y,
Align horizontal,
Align vertical,
Font font,
const char* text) {
furi_assert(widget);
WidgetElement* string_multi_element =
widget_element_string_multi_create(x, y, horizontal, vertical, font, text);
widget_add_element(widget, string_multi_element);
}
void widget_add_string_element(
Widget* widget,
uint8_t x,

View File

@ -26,6 +26,23 @@ void widget_clear(Widget* widget);
*/
View* widget_get_view(Widget* widget);
/** Add Multi String Element
* @param widget Widget instance
* @param x - x coordinate
* @param y - y coordinate
* @param horizontal - Align instance
* @param vertical - Align instance
* @param font Font instance
*/
void widget_add_string_multi_element(
Widget* widget,
uint8_t x,
uint8_t y,
Align horizontal,
Align vertical,
Font font,
const char* text);
/** Add String Element
* @param widget Widget instance
* @param x - x coordinate

View File

@ -30,7 +30,18 @@ static bool gui_button_input(InputEvent* event, WidgetElement* element) {
GuiButtonModel* model = element->model;
bool consumed = false;
if((event->type == InputTypeShort) && model->callback) {
if(model->callback == NULL) return consumed;
if(event->key == InputKeyOk && event->type == InputTypePress &&
model->button_type == GuiButtonTypeCenter) {
model->callback(GuiButtonTypeCenterPress, model->context);
consumed = true;
} else if(
event->key == InputKeyOk && event->type == InputTypeRelease &&
model->button_type == GuiButtonTypeCenter) {
model->callback(GuiButtonTypeCenterRelease, model->context);
consumed = true;
} else if(event->type == InputTypeShort) {
if((model->button_type == GuiButtonTypeLeft) && (event->key == InputKeyLeft)) {
model->callback(model->button_type, model->context);
consumed = true;

View File

@ -6,6 +6,8 @@ typedef enum {
GuiButtonTypeLeft,
GuiButtonTypeCenter,
GuiButtonTypeRight,
GuiButtonTypeCenterPress,
GuiButtonTypeCenterRelease,
} GuiButtonType;
typedef void (*ButtonCallback)(GuiButtonType result, void* context);
@ -28,6 +30,15 @@ struct WidgetElement {
Widget* parent;
};
/* Create multi string element */
WidgetElement* widget_element_string_multi_create(
uint8_t x,
uint8_t y,
Align horizontal,
Align vertical,
Font font,
const char* text);
/* Create string element */
WidgetElement* widget_element_string_create(
uint8_t x,

View File

@ -0,0 +1,67 @@
#include "widget_element_i.h"
#include <m-string.h>
#include <gui/elements.h>
typedef struct {
uint8_t x;
uint8_t y;
Align horizontal;
Align vertical;
Font font;
string_t text;
} GuiStringMultiModel;
static void gui_string_multi_draw(Canvas* canvas, WidgetElement* element) {
furi_assert(canvas);
furi_assert(element);
GuiStringMultiModel* model = element->model;
if(string_size(model->text)) {
canvas_set_font(canvas, model->font);
elements_multiline_text_aligned(
canvas,
model->x,
model->y,
model->horizontal,
model->vertical,
string_get_cstr(model->text));
}
}
static void gui_string_multi_free(WidgetElement* gui_string) {
furi_assert(gui_string);
GuiStringMultiModel* model = gui_string->model;
string_clear(model->text);
free(gui_string->model);
free(gui_string);
}
WidgetElement* widget_element_string_multi_create(
uint8_t x,
uint8_t y,
Align horizontal,
Align vertical,
Font font,
const char* text) {
furi_assert(text);
// Allocate and init model
GuiStringMultiModel* model = furi_alloc(sizeof(GuiStringMultiModel));
model->x = x;
model->y = y;
model->horizontal = horizontal;
model->vertical = vertical;
model->font = font;
string_init_set_str(model->text, text);
// Allocate and init Element
WidgetElement* gui_string = furi_alloc(sizeof(WidgetElement));
gui_string->parent = NULL;
gui_string->input = NULL;
gui_string->draw = gui_string_multi_draw;
gui_string->free = gui_string_multi_free;
gui_string->model = model;
return gui_string;
}

View File

@ -25,9 +25,9 @@ static void signal_received_callback(void* context, IrdaWorkerSignal* received_s
sizeof(buf),
"%s, A:0x%0*lX, C:0x%0*lX%s\r\n",
irda_get_protocol_name(message->protocol),
irda_get_protocol_address_length(message->protocol),
ROUND_UP_TO(irda_get_protocol_address_length(message->protocol), 4),
message->address,
irda_get_protocol_command_length(message->protocol),
ROUND_UP_TO(irda_get_protocol_command_length(message->protocol), 4),
message->command,
message->repeat ? " R" : "");
cli_write(cli, (uint8_t*)buf, buf_cnt);
@ -98,6 +98,20 @@ static bool parse_message(const char* str, IrdaMessage* message) {
return false;
}
uint32_t address_length = irda_get_protocol_address_length(protocol);
uint32_t address_mask = (1LU << address_length) - 1;
if(address != (address & address_mask)) {
printf("Address out of range (mask 0x%08lX): 0x%lX\r\n", address_mask, address);
return false;
}
uint32_t command_length = irda_get_protocol_command_length(protocol);
uint32_t command_mask = (1LU << command_length) - 1;
if(command != (command & command_mask)) {
printf("Command out of range (mask 0x%08lX): 0x%lX\r\n", command_mask, command);
return false;
}
message->protocol = protocol;
message->address = address;
message->command = command;

View File

@ -45,9 +45,9 @@ size_t IrdaAppFileParser::stringify_message(
"%.31s %.31s A:%0*lX C:%0*lX\n",
name,
irda_get_protocol_name(protocol),
irda_get_protocol_address_length(protocol),
ROUND_UP_TO(irda_get_protocol_address_length(protocol), 4),
message.address,
irda_get_protocol_command_length(protocol),
ROUND_UP_TO(irda_get_protocol_command_length(protocol), 4),
message.command);
furi_assert(written < buf_size);
@ -162,8 +162,8 @@ std::unique_ptr<IrdaAppFileParser::IrdaFileSignal>
return nullptr;
}
int address_length = irda_get_protocol_address_length(protocol);
uint32_t address_mask = (1LU << (4 * address_length)) - 1;
uint32_t address_length = irda_get_protocol_address_length(protocol);
uint32_t address_mask = (1LU << address_length) - 1;
if(address != (address & address_mask)) {
size_t end_of_str = MIN(str.find_last_not_of(" \t\r\n") + 1, (size_t)30);
FURI_LOG_E(
@ -176,8 +176,8 @@ std::unique_ptr<IrdaAppFileParser::IrdaFileSignal>
return nullptr;
}
int command_length = irda_get_protocol_command_length(protocol);
uint32_t command_mask = (1LU << (4 * command_length)) - 1;
uint32_t command_length = irda_get_protocol_command_length(protocol);
uint32_t command_mask = (1LU << command_length) - 1;
if(command != (command & command_mask)) {
size_t end_of_str = MIN(str.find_last_not_of(" \t\r\n") + 1, (size_t)30);
FURI_LOG_E(

View File

@ -29,9 +29,9 @@ void IrdaAppSceneEditDelete::on_enter(IrdaApp* app) {
"%s\n%s\nA=0x%0*lX C=0x%0*lX",
remote_manager->get_button_name(app->get_current_button()).c_str(),
irda_get_protocol_name(message->protocol),
irda_get_protocol_address_length(message->protocol),
ROUND_UP_TO(irda_get_protocol_address_length(message->protocol), 4),
message->address,
irda_get_protocol_command_length(message->protocol),
ROUND_UP_TO(irda_get_protocol_command_length(message->protocol), 4),
message->command);
} else {
app->set_text_store(

View File

@ -13,7 +13,7 @@ void IrdaAppSceneLearnEnterName::on_enter(IrdaApp* app) {
0,
"%.4s_%0*lX",
irda_get_protocol_name(message->protocol),
irda_get_protocol_command_length(message->protocol),
ROUND_UP_TO(irda_get_protocol_command_length(message->protocol), 4),
message->command);
} else {
auto raw_signal = signal.get_raw_signal();

View File

@ -27,9 +27,9 @@ void IrdaAppSceneLearnSuccess::on_enter(IrdaApp* app) {
app->set_text_store(
1,
"A: 0x%0*lX\nC: 0x%0*lX\n",
irda_get_protocol_address_length(message->protocol),
ROUND_UP_TO(irda_get_protocol_address_length(message->protocol), 4),
message->address,
irda_get_protocol_command_length(message->protocol),
ROUND_UP_TO(irda_get_protocol_command_length(message->protocol), 4),
message->command);
dialog_ex_set_header(dialog_ex, app->get_text_store(0), 95, 10, AlignCenter, AlignCenter);
dialog_ex_set_text(dialog_ex, app->get_text_store(1), 75, 23, AlignLeft, AlignTop);

View File

@ -64,18 +64,18 @@ static void signal_received_callback(void* context, IrdaWorkerSignal* received_s
sizeof(irda_monitor->display_text),
"%s\nA:0x%0*lX\nC:0x%0*lX\n%s\n",
irda_get_protocol_name(message->protocol),
irda_get_protocol_address_length(message->protocol),
ROUND_UP_TO(irda_get_protocol_address_length(message->protocol), 4),
message->address,
irda_get_protocol_command_length(message->protocol),
ROUND_UP_TO(irda_get_protocol_command_length(message->protocol), 4),
message->command,
message->repeat ? " R" : "");
view_port_update(irda_monitor->view_port);
printf(
"== %s, A:0x%0*lX, C:0x%0*lX%s ==\r\n",
irda_get_protocol_name(message->protocol),
irda_get_protocol_address_length(message->protocol),
ROUND_UP_TO(irda_get_protocol_address_length(message->protocol), 4),
message->address,
irda_get_protocol_command_length(message->protocol),
ROUND_UP_TO(irda_get_protocol_command_length(message->protocol), 4),
message->command,
message->repeat ? " R" : "");
} else {

View File

@ -18,6 +18,7 @@
#include <stm32wbxx.h>
#include <notification/notification-messages.h>
#include <applications/bt/bt_service/bt.h>
#define POWER_OFF_TIMEOUT 30
@ -39,6 +40,7 @@ struct Power {
ValueMutex* menu_vm;
Cli* cli;
Bt* bt;
MenuItem* menu;
PowerState state;
@ -108,6 +110,8 @@ Power* power_alloc() {
power->cli = furi_record_open("cli");
power_cli_init(power->cli, power);
power->bt = furi_record_open("bt");
power->menu = menu_item_alloc_menu("Power", icon_animation_alloc(&A_Power_14));
menu_item_subitem_add(
power->menu, menu_item_alloc_function("Off", NULL, power_menu_off_callback, power));
@ -206,13 +210,15 @@ int32_t power_srv(void* p) {
power->menu_vm, (Menu * menu) { menu_item_add(menu, power->menu); });
furi_record_create("power", power);
uint8_t battery_level = 0;
uint8_t battery_level_prev = 0;
while(1) {
bool battery_low = false;
with_view_model(
power->info_view, (PowerInfoModel * model) {
model->charge = furi_hal_power_get_pct();
battery_level = model->charge;
model->health = furi_hal_power_get_bat_health_pct();
model->capacity_remaining = furi_hal_power_get_battery_remaining_capacity();
model->capacity_full = furi_hal_power_get_battery_full_capacity();
@ -258,6 +264,11 @@ int32_t power_srv(void* p) {
power_charging_indication_handler(power, notifications);
if(battery_level_prev != battery_level) {
battery_level_prev = battery_level;
bt_update_battery_level(power->bt, battery_level);
}
view_port_update(power->battery_view_port);
osDelay(1024);

View File

@ -1,10 +1,12 @@
ADD_SCENE(subghz, start, Start)
ADD_SCENE(subghz, receiver, Receiver)
ADD_SCENE(subghz, receiver_config, ReceiverConfig)
ADD_SCENE(subghz, receiver_info, ReceiverInfo)
ADD_SCENE(subghz, save_name, SaveName)
ADD_SCENE(subghz, save_success, SaveSuccess)
ADD_SCENE(subghz, saved, Saved)
ADD_SCENE(subghz, transmitter, Transmitter)
ADD_SCENE(subghz, no_man, NoMan)
ADD_SCENE(subghz, show_error, ShowError)
ADD_SCENE(subghz, test, Test)
ADD_SCENE(subghz, test_static, TestStatic)
ADD_SCENE(subghz, test_carrier, TestCarrier)

View File

@ -1,21 +1,102 @@
#include "../subghz_i.h"
#include "../views/subghz_receiver.h"
static void subghz_scene_receiver_update_statusbar(void* context) {
SubGhz* subghz = context;
char frequency_str[20];
char preset_str[10];
string_t history_stat_str;
string_init(history_stat_str);
if(!subghz_history_get_text_space_left(subghz->txrx->history, history_stat_str)) {
snprintf(
frequency_str,
sizeof(frequency_str),
"%03ld.%02ld",
subghz->txrx->frequency / 1000000 % 1000,
subghz->txrx->frequency / 10000 % 100);
if(subghz->txrx->preset == FuriHalSubGhzPresetOok650Async ||
subghz->txrx->preset == FuriHalSubGhzPresetOok270Async) {
snprintf(preset_str, sizeof(preset_str), "AM");
} else if(subghz->txrx->preset == FuriHalSubGhzPreset2FSKAsync) {
snprintf(preset_str, sizeof(preset_str), "FM");
} else {
furi_check(0);
}
subghz_receiver_add_data_statusbar(
subghz->subghz_receiver, frequency_str, preset_str, string_get_cstr(history_stat_str));
} else {
subghz_receiver_add_data_statusbar(
subghz->subghz_receiver, string_get_cstr(history_stat_str), "", "");
subghz->state_notifications = NOTIFICATION_IDLE_STATE;
}
string_clear(history_stat_str);
}
void subghz_scene_receiver_callback(SubghzReceverEvent event, void* context) {
furi_assert(context);
SubGhz* subghz = context;
view_dispatcher_send_custom_event(subghz->view_dispatcher, event);
}
void subghz_scene_add_to_history_callback(SubGhzProtocolCommon* parser, void* context) {
furi_assert(context);
SubGhz* subghz = context;
string_t str_buff;
string_init(str_buff);
if(subghz_history_add_to_history(
subghz->txrx->history, parser, subghz->txrx->frequency, subghz->txrx->preset)) {
subghz_protocol_reset(subghz->txrx->protocol);
string_clean(str_buff);
subghz_history_get_text_item_menu(
subghz->txrx->history, str_buff, subghz_history_get_item(subghz->txrx->history) - 1);
subghz_receiver_add_item_to_menu(
subghz->subghz_receiver,
string_get_cstr(str_buff),
subghz_history_get_type_protocol(
subghz->txrx->history, subghz_history_get_item(subghz->txrx->history) - 1));
subghz_scene_receiver_update_statusbar(subghz);
}
string_clear(str_buff);
}
const void subghz_scene_receiver_on_enter(void* context) {
SubGhz* subghz = context;
SubghzReceiver* subghz_receiver = subghz->subghz_receiver;
subghz_receiver_set_callback(subghz_receiver, subghz_scene_receiver_callback, subghz);
string_t str_buff;
string_init(str_buff);
//Load history to receiver
subghz_receiver_exit(subghz->subghz_receiver);
for(uint8_t i = 0; i < subghz_history_get_item(subghz->txrx->history); i++) {
string_clean(str_buff);
subghz_history_get_text_item_menu(subghz->txrx->history, str_buff, i);
subghz_receiver_add_item_to_menu(
subghz->subghz_receiver,
string_get_cstr(str_buff),
subghz_history_get_type_protocol(subghz->txrx->history, i));
}
string_clear(str_buff);
subghz_scene_receiver_update_statusbar(subghz);
subghz_receiver_set_callback(subghz->subghz_receiver, subghz_scene_receiver_callback, subghz);
subghz_protocol_enable_dump(
subghz->txrx->protocol, subghz_scene_add_to_history_callback, subghz);
subghz_receiver_set_protocol(subghz_receiver, subghz->protocol_result, subghz->protocol);
subghz_receiver_set_worker(subghz_receiver, subghz->worker);
subghz->state_notifications = NOTIFICATION_RX_STATE;
if(subghz->txrx->txrx_state == SubGhzTxRxStateRx) {
subghz_rx_end(subghz->txrx->worker);
//subghz_sleep();
subghz->txrx->txrx_state = SubGhzTxRxStateIdle;
};
if(subghz->txrx->txrx_state == SubGhzTxRxStateIdle) {
subghz_begin(subghz->txrx->preset);
subghz_rx(subghz->txrx->worker, subghz->txrx->frequency);
subghz->txrx->txrx_state = SubGhzTxRxStateRx;
}
if(subghz->txrx->idx_menu_chosen != 0) {
subghz_receiver_set_idx_menu(subghz->subghz_receiver, subghz->txrx->idx_menu_chosen);
}
view_dispatcher_switch_to_view(subghz->view_dispatcher, SubGhzViewReceiver);
}
@ -24,51 +105,43 @@ const bool subghz_scene_receiver_on_event(void* context, SceneManagerEvent event
if(event.type == SceneManagerEventTypeCustom) {
switch(event.event) {
case SubghzReceverEventSave:
subghz->state_notifications = NOTIFICATION_IDLE_STATE;
subghz->frequency = subghz_receiver_get_frequency(subghz->subghz_receiver);
subghz->preset = subghz_receiver_get_preset(subghz->subghz_receiver);
subghz->protocol_result = subghz_receiver_get_protocol(subghz->subghz_receiver);
scene_manager_next_scene(subghz->scene_manager, SubGhzSceneSaveName);
return true;
break;
case SubghzReceverEventBack:
scene_manager_previous_scene(subghz->scene_manager);
// Stop CC1101 Rx
if(subghz->txrx->txrx_state == SubGhzTxRxStateRx) {
subghz_rx_end(subghz->txrx->worker);
subghz_sleep();
subghz->txrx->txrx_state = SubGhzTxRxStateIdle;
};
subghz_history_clean(subghz->txrx->history);
subghz->txrx->hopper_state = SubGhzHopperStateOFF;
subghz->txrx->frequency = subghz_frequencies[subghz_frequencies_433_92];
subghz->txrx->preset = FuriHalSubGhzPresetOok650Async;
subghz->txrx->idx_menu_chosen = 0;
subghz_protocol_enable_dump(subghz->txrx->protocol, NULL, subghz);
scene_manager_search_and_switch_to_previous_scene(
subghz->scene_manager, SubGhzSceneStart);
return true;
break;
case SubghzReceverEventSendStart:
subghz->state_notifications = NOTIFICATION_TX_STATE;
subghz->frequency = subghz_receiver_get_frequency(subghz->subghz_receiver);
subghz->preset = subghz_receiver_get_preset(subghz->subghz_receiver);
subghz->protocol_result = subghz_receiver_get_protocol(subghz->subghz_receiver);
subghz_transmitter_tx_start(subghz);
return true;
break;
case SubghzReceverEventSendStop:
subghz->state_notifications = NOTIFICATION_IDLE_STATE;
subghz_transmitter_tx_stop(subghz);
return true;
break;
case SubghzReceverEventMain:
subghz->state_notifications = NOTIFICATION_RX_STATE;
case SubghzReceverEventOK:
subghz->txrx->idx_menu_chosen = subghz_receiver_get_idx_menu(subghz->subghz_receiver);
scene_manager_next_scene(subghz->scene_manager, SubGhzSceneReceiverInfo);
return true;
break;
case SubghzReceverEventConfig:
subghz->state_notifications = NOTIFICATION_IDLE_STATE;
return true;
break;
case SubghzReceverEventSendHistoryFull:
subghz->state_notifications = NOTIFICATION_IDLE_STATE;
scene_manager_next_scene(subghz->scene_manager, SubGhzSceneReceiverConfig);
return true;
break;
default:
break;
}
} else if(event.type == SceneManagerEventTypeTick) {
if(subghz->txrx->hopper_state != SubGhzHopperStateOFF) {
subghz_hopper_update(subghz->txrx);
subghz_scene_receiver_update_statusbar(subghz);
}
switch(subghz->state_notifications) {
case NOTIFICATION_TX_STATE:
notification_message(subghz->notifications, &sequence_blink_red_10);
break;
case NOTIFICATION_RX_STATE:
notification_message(subghz->notifications, &sequence_blink_blue_10);
break;

View File

@ -0,0 +1,156 @@
#include "../subghz_i.h"
#define PRESET_COUNT 3
const char* const preset_text[PRESET_COUNT] = {
"AM270",
"AM650",
"FM",
};
const uint32_t preset_value[PRESET_COUNT] = {
FuriHalSubGhzPresetOok270Async, /** OOK, bandwidth 270kHz, asynchronous */
FuriHalSubGhzPresetOok650Async, /** OOK, bandwidth 650kHz, asynchronous */
FuriHalSubGhzPreset2FSKAsync, /** FM, asynchronous */
};
#define HOPPING_COUNT 2
const char* const hopping_text[HOPPING_COUNT] = {
"OFF",
"ON",
};
const uint32_t hopping_value[HOPPING_COUNT] = {
SubGhzHopperStateOFF,
SubGhzHopperStateRunnig,
};
uint8_t subghz_scene_receiver_config_uint32_value_index(
const uint32_t value,
const uint32_t values[],
uint8_t values_count) {
int64_t last_value = INT64_MIN;
uint8_t index = 0;
for(uint8_t i = 0; i < values_count; i++) {
if((value >= last_value) && (value <= values[i])) {
index = i;
break;
}
last_value = values[i];
}
return index;
}
uint8_t subghz_scene_receiver_config_hopper_value_index(
const uint32_t value,
const uint32_t values[],
uint8_t values_count,
void* context) {
furi_assert(context);
SubGhz* subghz = context;
if(value == values[0]) {
return 0;
} else {
variable_item_set_current_value_text(
(VariableItem*)scene_manager_get_scene_state(
subghz->scene_manager, SubGhzSceneReceiverConfig),
" -----");
return 1;
}
}
static void subghz_scene_receiver_config_set_frequency(VariableItem* item) {
SubGhz* subghz = variable_item_get_context(item);
uint8_t index = variable_item_get_current_value_index(item);
if(subghz->txrx->hopper_state == SubGhzHopperStateOFF) {
variable_item_set_current_value_text(item, subghz_frequencies_text[index]);
subghz->txrx->frequency = subghz_frequencies[index];
}
}
static void subghz_scene_receiver_config_set_preset(VariableItem* item) {
SubGhz* subghz = variable_item_get_context(item);
uint8_t index = variable_item_get_current_value_index(item);
variable_item_set_current_value_text(item, preset_text[index]);
subghz->txrx->preset = preset_value[index];
}
static void subghz_scene_receiver_config_set_hopping_runing(VariableItem* item) {
SubGhz* subghz = variable_item_get_context(item);
uint8_t index = variable_item_get_current_value_index(item);
variable_item_set_current_value_text(item, hopping_text[index]);
if(hopping_value[index] == SubGhzHopperStateOFF) {
variable_item_set_current_value_text(
(VariableItem*)scene_manager_get_scene_state(
subghz->scene_manager, SubGhzSceneReceiverConfig),
subghz_frequencies_text[subghz_frequencies_433_92]);
subghz->txrx->frequency = subghz_frequencies[subghz_frequencies_433_92];
} else {
variable_item_set_current_value_text(
(VariableItem*)scene_manager_get_scene_state(
subghz->scene_manager, SubGhzSceneReceiverConfig),
" -----");
}
subghz->txrx->hopper_state = hopping_value[index];
}
void subghz_scene_receiver_config_callback(SubghzReceverEvent event, void* context) {
furi_assert(context);
SubGhz* subghz = context;
view_dispatcher_send_custom_event(subghz->view_dispatcher, event);
}
const void subghz_scene_receiver_config_on_enter(void* context) {
SubGhz* subghz = context;
VariableItem* item;
uint8_t value_index;
item = variable_item_list_add(
subghz->variable_item_list,
"Frequency:",
subghz_frequencies_count,
subghz_scene_receiver_config_set_frequency,
subghz);
value_index = subghz_scene_receiver_config_uint32_value_index(
subghz->txrx->frequency, subghz_frequencies, subghz_frequencies_count);
scene_manager_set_scene_state(
subghz->scene_manager, SubGhzSceneReceiverConfig, (uint32_t)item);
variable_item_set_current_value_index(item, value_index);
variable_item_set_current_value_text(item, subghz_frequencies_text[value_index]);
item = variable_item_list_add(
subghz->variable_item_list,
"Hopping:",
HOPPING_COUNT,
subghz_scene_receiver_config_set_hopping_runing,
subghz);
value_index = subghz_scene_receiver_config_hopper_value_index(
subghz->txrx->hopper_state, hopping_value, HOPPING_COUNT, subghz);
variable_item_set_current_value_index(item, value_index);
variable_item_set_current_value_text(item, hopping_text[value_index]);
item = variable_item_list_add(
subghz->variable_item_list,
"Modulation:",
PRESET_COUNT,
subghz_scene_receiver_config_set_preset,
subghz);
value_index = subghz_scene_receiver_config_uint32_value_index(
subghz->txrx->preset, preset_value, PRESET_COUNT);
variable_item_set_current_value_index(item, value_index);
variable_item_set_current_value_text(item, preset_text[value_index]);
view_dispatcher_switch_to_view(subghz->view_dispatcher, SubGhzViewVariableItemList);
}
const bool subghz_scene_receiver_config_on_event(void* context, SceneManagerEvent event) {
//SubGhz* subghz = context;
return false;
}
const void subghz_scene_receiver_config_on_exit(void* context) {
SubGhz* subghz = context;
variable_item_list_clean(subghz->variable_item_list);
}

View File

@ -0,0 +1,163 @@
#include "../subghz_i.h"
void subghz_scene_receiver_info_callback(GuiButtonType result, void* context) {
furi_assert(context);
SubGhz* subghz = context;
view_dispatcher_send_custom_event(subghz->view_dispatcher, result);
}
static bool subghz_scene_receiver_info_update_parser(void* context) {
SubGhz* subghz = context;
subghz->txrx->protocol_result = subghz_protocol_get_by_name(
subghz->txrx->protocol,
subghz_history_get_name(subghz->txrx->history, subghz->txrx->idx_menu_chosen));
if(subghz->txrx->protocol_result->to_load_protocol != NULL) {
subghz->txrx->protocol_result->to_load_protocol(
subghz->txrx->protocol_result,
subghz_history_get_raw_data(subghz->txrx->history, subghz->txrx->idx_menu_chosen));
subghz->txrx->frequency =
subghz_history_get_frequency(subghz->txrx->history, subghz->txrx->idx_menu_chosen);
subghz->txrx->preset =
subghz_history_get_preset(subghz->txrx->history, subghz->txrx->idx_menu_chosen);
return true;
}
return false;
}
const void subghz_scene_receiver_info_on_enter(void* context) {
SubGhz* subghz = context;
if(subghz_scene_receiver_info_update_parser(subghz)) {
char buffer_str[16];
snprintf(
buffer_str,
sizeof(buffer_str),
"%03ld.%02ld",
subghz->txrx->frequency / 1000000 % 1000,
subghz->txrx->frequency / 10000 % 100);
widget_add_string_element(
subghz->widget, 78, 0, AlignLeft, AlignTop, FontSecondary, buffer_str);
if(subghz->txrx->preset == FuriHalSubGhzPresetOok650Async ||
subghz->txrx->preset == FuriHalSubGhzPresetOok270Async) {
snprintf(buffer_str, sizeof(buffer_str), "AM");
} else if(subghz->txrx->preset == FuriHalSubGhzPreset2FSKAsync) {
snprintf(buffer_str, sizeof(buffer_str), "FM");
} else {
furi_check(0);
}
widget_add_string_element(
subghz->widget, 113, 0, AlignLeft, AlignTop, FontSecondary, buffer_str);
string_t text;
string_init(text);
subghz->txrx->protocol_result->to_string(subghz->txrx->protocol_result, text);
widget_add_string_multi_element(
subghz->widget, 0, 0, AlignLeft, AlignTop, FontSecondary, string_get_cstr(text));
string_clear(text);
if(subghz->txrx->protocol_result && subghz->txrx->protocol_result->to_save_string &&
strcmp(subghz->txrx->protocol_result->name, "KeeLoq")) {
widget_add_button_element(
subghz->widget,
GuiButtonTypeRight,
"Save",
subghz_scene_receiver_info_callback,
subghz);
widget_add_button_element(
subghz->widget,
GuiButtonTypeCenter,
"Send",
subghz_scene_receiver_info_callback,
subghz);
}
} else {
widget_add_icon_element(subghz->widget, 32, 12, &I_DolphinFirstStart7_61x51);
widget_add_string_element(
subghz->widget, 13, 8, AlignLeft, AlignBottom, FontSecondary, "Error history parse.");
}
view_dispatcher_switch_to_view(subghz->view_dispatcher, SubGhzViewWidget);
}
const bool subghz_scene_receiver_info_on_event(void* context, SceneManagerEvent event) {
SubGhz* subghz = context;
if(event.type == SceneManagerEventTypeCustom) {
if(event.event == GuiButtonTypeCenterPress) {
//CC1101 Stop RX -> Start TX
subghz->state_notifications = NOTIFICATION_TX_STATE;
if(subghz->txrx->hopper_state != SubGhzHopperStateOFF) {
subghz->txrx->hopper_state = SubGhzHopperStatePause;
}
if(subghz->txrx->txrx_state == SubGhzTxRxStateRx) {
subghz_rx_end(subghz->txrx->worker);
//subghz_sleep();
subghz->txrx->txrx_state = SubGhzTxRxStateIdle;
}
if(!subghz_scene_receiver_info_update_parser(subghz)) {
return false;
}
if(subghz->txrx->txrx_state == SubGhzTxRxStateIdle) {
subghz_tx_start(subghz);
subghz->txrx->txrx_state = SubGhzTxRxStateTx;
}
return true;
} else if(event.event == GuiButtonTypeCenterRelease) {
//CC1101 Stop Tx -> Start RX
subghz->state_notifications = NOTIFICATION_IDLE_STATE;
if(subghz->txrx->txrx_state == SubGhzTxRxStateTx) {
subghz_tx_stop(subghz);
subghz->txrx->txrx_state = SubGhzTxRxStateIdle;
}
if(subghz->txrx->txrx_state == SubGhzTxRxStateIdle) {
subghz_begin(subghz->txrx->preset);
subghz_rx(subghz->txrx->worker, subghz->txrx->frequency);
subghz->txrx->txrx_state = SubGhzTxRxStateRx;
}
if(subghz->txrx->hopper_state == SubGhzHopperStatePause) {
subghz->txrx->hopper_state = SubGhzHopperStateRunnig;
}
subghz->state_notifications = NOTIFICATION_RX_STATE;
return true;
} else if(event.event == GuiButtonTypeRight) {
//CC1101 Stop RX -> Save
subghz->state_notifications = NOTIFICATION_IDLE_STATE;
if(subghz->txrx->hopper_state != SubGhzHopperStateOFF) {
subghz->txrx->hopper_state = SubGhzHopperStateOFF;
}
if(subghz->txrx->txrx_state == SubGhzTxRxStateRx) {
subghz_rx_end(subghz->txrx->worker);
subghz_sleep();
subghz->txrx->txrx_state = SubGhzTxRxStateIdle;
}
if(!subghz_scene_receiver_info_update_parser(subghz)) {
return false;
}
if(subghz->txrx->protocol_result && subghz->txrx->protocol_result->to_save_string &&
strcmp(subghz->txrx->protocol_result->name, "KeeLoq")) {
scene_manager_next_scene(subghz->scene_manager, SubGhzSceneSaveName);
}
return true;
}
} else if(event.type == SceneManagerEventTypeTick) {
if(subghz->txrx->hopper_state != SubGhzHopperStateOFF) {
subghz_hopper_update(subghz->txrx);
}
switch(subghz->state_notifications) {
case NOTIFICATION_TX_STATE:
notification_message(subghz->notifications, &sequence_blink_red_10);
break;
case NOTIFICATION_RX_STATE:
notification_message(subghz->notifications, &sequence_blink_blue_10);
break;
default:
break;
}
}
return false;
}
const void subghz_scene_receiver_info_on_exit(void* context) {
SubGhz* subghz = context;
widget_clear(subghz->widget);
}

View File

@ -35,11 +35,13 @@ const bool subghz_scene_save_name_on_event(void* context, SceneManagerEvent even
if(event.type == SceneManagerEventTypeCustom) {
if(event.event == SCENE_SAVE_NAME_CUSTOM_EVENT) {
if(subghz_save_protocol_to_file(subghz, subghz->text_store)) {
if(strcmp(subghz->text_store, "") &&
subghz_save_protocol_to_file(subghz, subghz->text_store)) {
scene_manager_next_scene(subghz->scene_manager, SubGhzSceneSaveSuccess);
return true;
} else {
//Error save
string_set(subghz->error_str, "No name file");
scene_manager_next_scene(subghz->scene_manager, SubGhzSceneShowError);
return true;
}
}

View File

@ -8,15 +8,18 @@ enum SubmenuIndex {
SubmenuIndexCAME12bit,
SubmenuIndexCAME24bit,
SubmenuIndexNeroSketch,
SubmenuIndexNeroRadio,
SubmenuIndexGateTX,
SubmenuIndexDoorHan,
};
bool subghz_scene_set_type_submenu_to_find_protocol(void* context, const char* protocol_name) {
SubGhz* subghz = context;
subghz->protocol_result = subghz_protocol_get_by_name(subghz->protocol, protocol_name);
if(subghz->protocol_result == NULL) {
//show error
subghz->txrx->protocol_result =
subghz_protocol_get_by_name(subghz->txrx->protocol, protocol_name);
if(subghz->txrx->protocol_result == NULL) {
string_set(subghz->error_str, "Protocol not found");
scene_manager_next_scene(subghz->scene_manager, SubGhzSceneShowError);
return false;
}
return true;
@ -62,6 +65,8 @@ const void subghz_scene_set_type_on_enter(void* context) {
subghz);
// submenu_add_item(
// subghz->submenu, "Nero Sketch", SubmenuIndexNeroSketch, subghz_scene_set_type_submenu_callback, subghz);
// submenu_add_item(
// subghz->submenu, "Nero Radio", SubmenuIndexNeroRadio, subghz_scene_set_type_submenu_callback, subghz);
submenu_add_item(
subghz->submenu,
"Gate TX_433",
@ -90,81 +95,86 @@ const bool subghz_scene_set_type_on_event(void* context, SceneManagerEvent event
switch(event.event) {
case SubmenuIndexPricenton:
if(subghz_scene_set_type_submenu_to_find_protocol(subghz, "Princeton")) {
subghz->protocol_result->code_last_count_bit = 24;
subghz->txrx->protocol_result->code_last_count_bit = 24;
key = (key & 0x00FFFFF0) | 0x4; //btn 0x1, 0x2, 0x4, 0x8
subghz->protocol_result->code_last_found = key;
subghz->txrx->protocol_result->code_last_found = key;
generated_protocol = true;
}
break;
case SubmenuIndexNiceFlo12bit:
if(subghz_scene_set_type_submenu_to_find_protocol(subghz, "Nice FLO")) {
subghz->protocol_result->code_last_count_bit = 12;
subghz->txrx->protocol_result->code_last_count_bit = 12;
key = (key & 0x0000FFF0) | 0x1; //btn 0x1, 0x2, 0x4
subghz->protocol_result->code_last_found = key;
subghz->txrx->protocol_result->code_last_found = key;
generated_protocol = true;
}
break;
case SubmenuIndexNiceFlo24bit:
if(subghz_scene_set_type_submenu_to_find_protocol(subghz, "Nice FLO")) {
subghz->protocol_result->code_last_count_bit = 24;
subghz->txrx->protocol_result->code_last_count_bit = 24;
key = (key & 0x00FFFFF0) | 0x4; //btn 0x1, 0x2, 0x4, 0x8
subghz->protocol_result->code_last_found = key;
subghz->txrx->protocol_result->code_last_found = key;
generated_protocol = true;
}
break;
case SubmenuIndexCAME12bit:
if(subghz_scene_set_type_submenu_to_find_protocol(subghz, "CAME")) {
subghz->protocol_result->code_last_count_bit = 12;
subghz->txrx->protocol_result->code_last_count_bit = 12;
key = (key & 0x0000FFF0) | 0x1; //btn 0x1, 0x2, 0x4
subghz->protocol_result->code_last_found = key;
subghz->txrx->protocol_result->code_last_found = key;
generated_protocol = true;
}
break;
case SubmenuIndexCAME24bit:
if(subghz_scene_set_type_submenu_to_find_protocol(subghz, "CAME")) {
subghz->protocol_result->code_last_count_bit = 24;
subghz->txrx->protocol_result->code_last_count_bit = 24;
key = (key & 0x00FFFFF0) | 0x4; //btn 0x1, 0x2, 0x4, 0x8
subghz->protocol_result->code_last_found = key;
subghz->txrx->protocol_result->code_last_found = key;
generated_protocol = true;
}
break;
// case SubmenuIndexNeroSketch:
// /* code */
// break;
// case SubmenuIndexNeroRadio:
// /* code */
// break;
case SubmenuIndexGateTX:
if(subghz_scene_set_type_submenu_to_find_protocol(subghz, "GateTX")) {
subghz->protocol_result->code_last_count_bit = 24;
subghz->txrx->protocol_result->code_last_count_bit = 24;
key = (key & 0x00F0FFFF) | 0xF << 16; //btn 0xF, 0xC, 0xA, 0x6
subghz->protocol_result->code_last_found = subghz_protocol_common_reverse_key(
key, subghz->protocol_result->code_last_count_bit);
subghz->txrx->protocol_result->code_last_found =
subghz_protocol_common_reverse_key(
key, subghz->txrx->protocol_result->code_last_count_bit);
generated_protocol = true;
}
break;
case SubmenuIndexDoorHan:
if(subghz_scene_set_type_submenu_to_find_protocol(subghz, "KeeLoq")) {
subghz->protocol_result->code_last_count_bit = 64;
subghz->protocol_result->serial = key & 0x0FFFFFFF;
subghz->protocol_result->btn = 0x2; //btn 0x1, 0x2, 0x4, 0x8
subghz->protocol_result->cnt = 0x0003;
subghz->txrx->protocol_result->code_last_count_bit = 64;
subghz->txrx->protocol_result->serial = key & 0x0FFFFFFF;
subghz->txrx->protocol_result->btn = 0x2; //btn 0x1, 0x2, 0x4, 0x8
subghz->txrx->protocol_result->cnt = 0x0003;
if(subghz_protocol_keeloq_set_manufacture_name(
subghz->protocol_result, "DoorHan")) {
subghz->protocol_result->code_last_found =
subghz_protocol_keeloq_gen_key(subghz->protocol_result);
subghz->txrx->protocol_result, "DoorHan")) {
subghz->txrx->protocol_result->code_last_found =
subghz_protocol_keeloq_gen_key(subghz->txrx->protocol_result);
generated_protocol = true;
} else {
generated_protocol = false;
scene_manager_next_scene(subghz->scene_manager, SubGhzSceneNoMan);
string_set(subghz->error_str, "No manufactory key");
scene_manager_next_scene(subghz->scene_manager, SubGhzSceneShowError);
}
}
break;
default:
return false;
break;
}
if(generated_protocol) {
subghz->frequency = subghz_frequencies[subghz_frequencies_433_92];
subghz->preset = FuriHalSubGhzPresetOok650Async;
subghz->txrx->frequency = subghz_frequencies[subghz_frequencies_433_92];
subghz->txrx->preset = FuriHalSubGhzPresetOok650Async;
scene_manager_next_scene(subghz->scene_manager, SubGhzSceneSaveName);
return true;
}

View File

@ -2,26 +2,26 @@
#define SCENE_NO_MAN_CUSTOM_EVENT (11UL)
void subghz_scene_no_man_popup_callback(void* context) {
void subghz_scene_show_error_popup_callback(void* context) {
SubGhz* subghz = context;
view_dispatcher_send_custom_event(subghz->view_dispatcher, SCENE_NO_MAN_CUSTOM_EVENT);
}
const void subghz_scene_no_man_on_enter(void* context) {
const void subghz_scene_show_error_on_enter(void* context) {
SubGhz* subghz = context;
// Setup view
Popup* popup = subghz->popup;
popup_set_icon(popup, 32, 12, &I_DolphinFirstStart7_61x51);
popup_set_header(popup, "No manufactory key", 13, 8, AlignLeft, AlignBottom);
popup_set_header(popup, string_get_cstr(subghz->error_str), 64, 8, AlignCenter, AlignBottom);
popup_set_timeout(popup, 1500);
popup_set_context(popup, subghz);
popup_set_callback(popup, subghz_scene_no_man_popup_callback);
popup_set_callback(popup, subghz_scene_show_error_popup_callback);
popup_enable_timeout(popup);
view_dispatcher_switch_to_view(subghz->view_dispatcher, SubGhzViewPopup);
}
const bool subghz_scene_no_man_on_event(void* context, SceneManagerEvent event) {
const bool subghz_scene_show_error_on_event(void* context, SceneManagerEvent event) {
SubGhz* subghz = context;
if(event.type == SceneManagerEventTypeCustom) {
if(event.event == SCENE_NO_MAN_CUSTOM_EVENT) {
@ -33,7 +33,7 @@ const bool subghz_scene_no_man_on_event(void* context, SceneManagerEvent event)
return false;
}
const void subghz_scene_no_man_on_exit(void* context) {
const void subghz_scene_show_error_on_exit(void* context) {
SubGhz* subghz = context;
// Clear view
@ -45,4 +45,5 @@ const void subghz_scene_no_man_on_exit(void* context) {
popup_set_context(popup, NULL);
popup_set_timeout(popup, 0);
popup_disable_timeout(popup);
string_clean(subghz->error_str);
}

View File

@ -1,5 +1,6 @@
#include "../subghz_i.h"
#include "../views/subghz_transmitter.h"
#include <lib/subghz/protocols/subghz_protocol_keeloq.h>
void subghz_scene_transmitter_callback(SubghzTransmitterEvent event, void* context) {
furi_assert(context);
@ -7,42 +8,90 @@ void subghz_scene_transmitter_callback(SubghzTransmitterEvent event, void* conte
view_dispatcher_send_custom_event(subghz->view_dispatcher, event);
}
static void subghz_scene_transmitter_update_data_show(void* context) {
SubGhz* subghz = context;
if(subghz->txrx->protocol_result && subghz->txrx->protocol_result->get_upload_protocol) {
string_t key_str;
string_init(key_str);
char frequency_str[10];
char preset_str[6];
uint8_t show_button = 0;
subghz->txrx->protocol_result->to_string(subghz->txrx->protocol_result, key_str);
if((!strcmp(subghz->txrx->protocol_result->name, "KeeLoq")) &&
(!strcmp(
subghz_protocol_keeloq_get_manufacture_name(subghz->txrx->protocol_result),
"Unknown"))) {
show_button = 0;
} else {
show_button = 1;
}
snprintf(
frequency_str,
sizeof(frequency_str),
"%03ld.%02ld",
subghz->txrx->frequency / 1000000 % 1000,
subghz->txrx->frequency / 10000 % 100);
if(subghz->txrx->preset == FuriHalSubGhzPresetOok650Async ||
subghz->txrx->preset == FuriHalSubGhzPresetOok270Async) {
snprintf(preset_str, sizeof(preset_str), "AM");
} else if(subghz->txrx->preset == FuriHalSubGhzPreset2FSKAsync) {
snprintf(preset_str, sizeof(preset_str), "FM");
} else {
furi_check(0);
}
subghz_transmitter_add_data_to_show(
subghz->subghz_transmitter,
string_get_cstr(key_str),
frequency_str,
preset_str,
show_button);
string_clear(key_str);
} else {
string_set(subghz->error_str, "Protocol not found");
scene_manager_next_scene(subghz->scene_manager, SubGhzSceneShowError);
}
}
const void subghz_scene_transmitter_on_enter(void* context) {
SubGhz* subghz = context;
SubghzTransmitter* subghz_transmitter = subghz->subghz_transmitter;
subghz_transmitter_set_callback(subghz_transmitter, subghz_scene_transmitter_callback, subghz);
subghz_transmitter_set_protocol(subghz_transmitter, subghz->protocol_result);
subghz_transmitter_set_frequency_preset(subghz_transmitter, subghz->frequency, subghz->preset);
view_dispatcher_switch_to_view(subghz->view_dispatcher, SubGhzViewTransmitter);
subghz_transmitter_set_callback(
subghz->subghz_transmitter, subghz_scene_transmitter_callback, subghz);
subghz_scene_transmitter_update_data_show(subghz);
subghz->state_notifications = NOTIFICATION_IDLE_STATE;
view_dispatcher_switch_to_view(subghz->view_dispatcher, SubGhzViewTransmitter);
}
const bool subghz_scene_transmitter_on_event(void* context, SceneManagerEvent event) {
SubGhz* subghz = context;
if(event.type == SceneManagerEventTypeCustom) {
if(event.event == SubghzTransmitterEventSendStart) {
subghz->state_notifications = NOTIFICATION_TX_STATE;
subghz_transmitter_tx_start(subghz);
if(subghz->txrx->txrx_state == SubGhzTxRxStateRx) {
subghz_rx_end(subghz->txrx->worker);
subghz->txrx->txrx_state = SubGhzTxRxStateIdle;
}
if(subghz->txrx->txrx_state == SubGhzTxRxStateIdle) {
subghz_tx_start(subghz);
subghz_scene_transmitter_update_data_show(subghz);
subghz->txrx->txrx_state = SubGhzTxRxStateTx;
}
return true;
} else if(event.event == SubghzTransmitterEventSendStop) {
subghz->state_notifications = NOTIFICATION_IDLE_STATE;
subghz_transmitter_tx_stop(subghz);
subghz_sleep();
if(subghz->txrx->txrx_state == SubGhzTxRxStateTx) {
subghz_tx_stop(subghz);
subghz_sleep();
subghz->txrx->txrx_state = SubGhzTxRxStateIdle;
}
return true;
} else if(event.event == SubghzTransmitterEventBack) {
subghz->state_notifications = NOTIFICATION_IDLE_STATE;
scene_manager_search_and_switch_to_previous_scene(
subghz->scene_manager, SubGhzSceneStart);
return true;
} else if(event.event == SubghzTransmitterEventNoMan) {
subghz->state_notifications = NOTIFICATION_IDLE_STATE;
scene_manager_search_and_switch_to_previous_scene(
subghz->scene_manager, SubGhzSceneNoMan);
return true;
}
} else if(event.type == SceneManagerEventTypeTick) {
if(subghz->state_notifications == NOTIFICATION_TX_STATE) {
@ -55,7 +104,6 @@ const bool subghz_scene_transmitter_on_event(void* context, SceneManagerEvent ev
const void subghz_scene_transmitter_on_exit(void* context) {
SubGhz* subghz = context;
SubghzTransmitter* subghz_transmitter = subghz->subghz_transmitter;
subghz_transmitter_set_callback(subghz_transmitter, NULL, subghz);
subghz->state_notifications = NOTIFICATION_IDLE_STATE;
}

View File

@ -1,5 +1,22 @@
#include "subghz_i.h"
const char* const subghz_frequencies_text[] = {
"300.00",
"315.00",
"348.00",
"387.00",
"433.08",
"433.92",
"434.78",
"438.90",
"464.00",
"779.00",
"868.35",
"915.00",
"925.00",
"928.00",
};
const uint32_t subghz_frequencies[] = {
/* 300 - 348 */
300000000,
@ -20,7 +37,15 @@ const uint32_t subghz_frequencies[] = {
928000000,
};
const uint32_t subghz_hopper_frequencies[] = {
315000000,
433920000,
868350000,
};
const uint32_t subghz_frequencies_count = sizeof(subghz_frequencies) / sizeof(uint32_t);
const uint32_t subghz_hopper_frequencies_count =
sizeof(subghz_hopper_frequencies) / sizeof(uint32_t);
const uint32_t subghz_frequencies_433_92 = 5;
bool subghz_custom_event_callback(void* context, uint32_t event) {
@ -77,11 +102,6 @@ SubGhz* subghz_alloc() {
SubGhzViewReceiver,
subghz_receiver_get_view(subghz->subghz_receiver));
// Dialog
subghz->dialog_ex = dialog_ex_alloc();
view_dispatcher_add_view(
subghz->view_dispatcher, SubGhzViewDialogEx, dialog_ex_get_view(subghz->dialog_ex));
// Popup
subghz->popup = popup_alloc();
view_dispatcher_add_view(
@ -92,6 +112,11 @@ SubGhz* subghz_alloc() {
view_dispatcher_add_view(
subghz->view_dispatcher, SubGhzViewTextInput, text_input_get_view(subghz->text_input));
// Custom Widget
subghz->widget = widget_alloc();
view_dispatcher_add_view(
subghz->view_dispatcher, SubGhzViewWidget, widget_get_view(subghz->widget));
// Transmitter
subghz->subghz_transmitter = subghz_transmitter_alloc();
view_dispatcher_add_view(
@ -99,6 +124,13 @@ SubGhz* subghz_alloc() {
SubGhzViewTransmitter,
subghz_transmitter_get_view(subghz->subghz_transmitter));
// Variable Item List
subghz->variable_item_list = variable_item_list_alloc();
view_dispatcher_add_view(
subghz->view_dispatcher,
SubGhzViewVariableItemList,
variable_item_list_get_view(subghz->variable_item_list));
// Carrier Test Module
subghz->subghz_test_carrier = subghz_test_carrier_alloc();
view_dispatcher_add_view(
@ -120,17 +152,26 @@ SubGhz* subghz_alloc() {
SubGhzViewStatic,
subghz_test_static_get_view(subghz->subghz_test_static));
//init Worker & Protocol
subghz->worker = subghz_worker_alloc();
subghz->protocol = subghz_protocol_alloc();
//init Worker & Protocol & History
subghz->txrx = furi_alloc(sizeof(SubGhzTxRx));
subghz->txrx->frequency = subghz_frequencies[subghz_frequencies_433_92];
subghz->txrx->preset = FuriHalSubGhzPresetOok650Async;
subghz->txrx->txrx_state = SubGhzTxRxStateIdle;
subghz->txrx->hopper_state = SubGhzHopperStateOFF;
subghz->txrx->history = subghz_history_alloc();
subghz->txrx->worker = subghz_worker_alloc();
subghz->txrx->protocol = subghz_protocol_alloc();
subghz_worker_set_overrun_callback(
subghz->worker, (SubGhzWorkerOverrunCallback)subghz_protocol_reset);
subghz->txrx->worker, (SubGhzWorkerOverrunCallback)subghz_protocol_reset);
subghz_worker_set_pair_callback(
subghz->worker, (SubGhzWorkerPairCallback)subghz_protocol_parse);
subghz_worker_set_context(subghz->worker, subghz->protocol);
subghz->txrx->worker, (SubGhzWorkerPairCallback)subghz_protocol_parse);
subghz_worker_set_context(subghz->txrx->worker, subghz->txrx->protocol);
subghz_protocol_load_keeloq_file(subghz->protocol, "/ext/subghz/keeloq_mfcodes");
subghz_protocol_load_nice_flor_s_file(subghz->protocol, "/ext/subghz/nice_floor_s_rx");
//Init Error_str
string_init(subghz->error_str);
subghz_protocol_load_keeloq_file(subghz->txrx->protocol, "/ext/subghz/keeloq_mfcodes");
subghz_protocol_load_nice_flor_s_file(subghz->txrx->protocol, "/ext/subghz/nice_floor_s_rx");
//subghz_protocol_enable_dump_text(subghz->protocol, subghz_text_callback, subghz);
@ -160,18 +201,22 @@ void subghz_free(SubGhz* subghz) {
view_dispatcher_remove_view(subghz->view_dispatcher, SubGhzViewTextInput);
text_input_free(subghz->text_input);
// Receiver
// Custom Widget
view_dispatcher_remove_view(subghz->view_dispatcher, SubGhzViewWidget);
widget_free(subghz->widget);
// Transmitter
view_dispatcher_remove_view(subghz->view_dispatcher, SubGhzViewTransmitter);
subghz_transmitter_free(subghz->subghz_transmitter);
// Variable Item List
view_dispatcher_remove_view(subghz->view_dispatcher, SubGhzViewVariableItemList);
variable_item_list_free(subghz->variable_item_list);
// Submenu
view_dispatcher_remove_view(subghz->view_dispatcher, SubGhzViewMenu);
submenu_free(subghz->submenu);
// DialogEx
view_dispatcher_remove_view(subghz->view_dispatcher, SubGhzViewDialogEx);
dialog_ex_free(subghz->dialog_ex);
// Popup
view_dispatcher_remove_view(subghz->view_dispatcher, SubGhzViewPopup);
popup_free(subghz->popup);
@ -186,9 +231,14 @@ void subghz_free(SubGhz* subghz) {
furi_record_close("gui");
subghz->gui = NULL;
//Worker & Protocol
subghz_protocol_free(subghz->protocol);
subghz_worker_free(subghz->worker);
//Worker & Protocol & History
subghz_protocol_free(subghz->txrx->protocol);
subghz_worker_free(subghz->txrx->worker);
subghz_history_free(subghz->txrx->history);
free(subghz->txrx);
//Error string
string_clear(subghz->error_str);
// Notifications
furi_record_close("notification");

View File

@ -128,21 +128,27 @@ void subghz_history_get_text_item_menu(SubGhzHistory* instance, string_t output,
}
}
void subghz_history_add_to_history(SubGhzHistory* instance, void* context) {
bool subghz_history_add_to_history(
SubGhzHistory* instance,
void* context,
uint32_t frequency,
FuriHalSubGhzPreset preset) {
furi_assert(instance);
furi_assert(context);
SubGhzProtocolCommon* protocol = context;
if(instance->last_index_write >= SUBGHZ_HISTORY_MAX) return;
if(instance->last_index_write >= SUBGHZ_HISTORY_MAX) return false;
if((instance->code_last_found == (protocol->code_last_found & 0xFFFF0FFFFFFFFFFF)) &&
((millis() - instance->last_update_timestamp) < 500)) {
instance->last_update_timestamp = millis();
return;
return false;
}
instance->code_last_found = protocol->code_last_found & 0xFFFF0FFFFFFFFFFF;
instance->last_update_timestamp = millis();
instance->history[instance->last_index_write].real_frequency = frequency;
instance->history[instance->last_index_write].preset = preset;
instance->history[instance->last_index_write].te = 0;
instance->history[instance->last_index_write].manufacture_name = NULL;
instance->history[instance->last_index_write].name = protocol->name;
@ -161,4 +167,5 @@ void subghz_history_add_to_history(SubGhzHistory* instance, void* context) {
instance->history[instance->last_index_write].type_protocol = protocol->type_protocol;
instance->last_index_write++;
return true;
}

View File

@ -1,3 +1,4 @@
#pragma once
#include <lib/subghz/protocols/subghz_protocol_common.h>
@ -94,8 +95,15 @@ bool subghz_history_get_text_space_left(SubGhzHistory* instance, string_t output
*
* @param instance - SubGhzHistory instance
* @param context - SubGhzProtocolCommon context
* @param frequency - frequency Hz
* @param preset - FuriHalSubGhzPreset preset
* @return bool;
*/
void subghz_history_add_to_history(SubGhzHistory* instance, void* context);
bool subghz_history_add_to_history(
SubGhzHistory* instance,
void* context,
uint32_t frequency,
FuriHalSubGhzPreset preset);
/** Get SubGhzProtocolCommonLoad to load into the protocol decoder bin data
*
@ -104,3 +112,5 @@ void subghz_history_add_to_history(SubGhzHistory* instance, void* context);
* @return SubGhzProtocolCommonLoad*
*/
SubGhzProtocolCommonLoad* subghz_history_get_raw_data(SubGhzHistory* instance, uint16_t idx);
void subghz_hopper_update(void* context);

View File

@ -19,6 +19,9 @@ void subghz_begin(FuriHalSubGhzPreset preset) {
uint32_t subghz_rx(void* context, uint32_t frequency) {
furi_assert(context);
if(!furi_hal_subghz_is_frequency_valid(frequency)) {
furi_check(0);
}
SubGhzWorker* worker = context;
furi_hal_subghz_idle();
@ -33,6 +36,9 @@ uint32_t subghz_rx(void* context, uint32_t frequency) {
}
uint32_t subghz_tx(uint32_t frequency) {
if(!furi_hal_subghz_is_frequency_valid(frequency)) {
furi_check(0);
}
furi_hal_subghz_idle();
uint32_t value = furi_hal_subghz_set_frequency_and_path(frequency);
hal_gpio_init(&gpio_cc1101_g0, GpioModeOutputPushPull, GpioPullNo, GpioSpeedLow);
@ -53,6 +59,7 @@ void subghz_rx_end(void* context) {
subghz_worker_stop(worker);
furi_hal_subghz_stop_async_rx();
}
furi_hal_subghz_idle();
}
void subghz_sleep(void) {
@ -66,41 +73,46 @@ void subghz_frequency_preset_to_str(void* context, string_t output) {
output,
"Frequency: %d\n"
"Preset: %d\n",
(int)subghz->frequency,
(int)subghz->preset);
(int)subghz->txrx->frequency,
(int)subghz->txrx->preset);
}
void subghz_transmitter_tx_start(void* context) {
void subghz_tx_start(void* context) {
furi_assert(context);
SubGhz* subghz = context;
subghz->encoder = subghz_protocol_encoder_common_alloc();
subghz->encoder->repeat = 200; //max repeat with the button held down
subghz->txrx->encoder = subghz_protocol_encoder_common_alloc();
subghz->txrx->encoder->repeat = 200; //max repeat with the button held down
//get upload
if(subghz->protocol_result->get_upload_protocol) {
if(subghz->protocol_result->get_upload_protocol(subghz->protocol_result, subghz->encoder)) {
if(subghz->preset) {
subghz_begin(subghz->preset);
if(subghz->txrx->protocol_result->get_upload_protocol) {
if(subghz->txrx->protocol_result->get_upload_protocol(
subghz->txrx->protocol_result, subghz->txrx->encoder)) {
if(subghz->txrx->preset) {
subghz_begin(subghz->txrx->preset);
} else {
subghz_begin(FuriHalSubGhzPresetOok650Async);
subghz_begin(FuriHalSubGhzPresetOok270Async);
}
if(subghz->frequency) {
subghz_tx(subghz->frequency);
if(subghz->txrx->frequency) {
subghz_tx(subghz->txrx->frequency);
} else {
subghz_tx(433920000);
}
//Start TX
furi_hal_subghz_start_async_tx(subghz_protocol_encoder_common_yield, subghz->encoder);
furi_hal_subghz_start_async_tx(
subghz_protocol_encoder_common_yield, subghz->txrx->encoder);
}
}
}
void subghz_transmitter_tx_stop(void* context) {
void subghz_tx_stop(void* context) {
furi_assert(context);
SubGhz* subghz = context;
//Stop TX
furi_hal_subghz_stop_async_tx();
subghz_protocol_encoder_common_free(subghz->encoder);
subghz_protocol_encoder_common_free(subghz->txrx->encoder);
furi_hal_subghz_idle();
//if protocol dynamic then we save the last upload
if(subghz->protocol_result->type_protocol == TYPE_PROTOCOL_DYNAMIC) {
if(subghz->txrx->protocol_result->type_protocol == TYPE_PROTOCOL_DYNAMIC) {
subghz_save_protocol_to_file(subghz, subghz->text_store);
}
notification_message(subghz->notifications, &sequence_reset_red);
@ -133,7 +145,7 @@ bool subghz_key_load(SubGhz* subghz, const char* file_path) {
if(res != 1) {
break;
}
subghz->frequency = (uint32_t)data;
subghz->txrx->frequency = (uint32_t)data;
// Read and parse preset from 2st line
if(!file_worker_read_until(file_worker, temp_str, '\n')) {
@ -143,7 +155,7 @@ bool subghz_key_load(SubGhz* subghz, const char* file_path) {
if(res != 1) {
break;
}
subghz->preset = (FuriHalSubGhzPreset)data;
subghz->txrx->preset = (FuriHalSubGhzPreset)data;
// Read and parse name protocol from 2st line
if(!file_worker_read_until(file_worker, temp_str, '\n')) {
@ -151,13 +163,13 @@ bool subghz_key_load(SubGhz* subghz, const char* file_path) {
}
// strlen("Protocol: ") = 10
string_right(temp_str, 10);
subghz->protocol_result =
subghz_protocol_get_by_name(subghz->protocol, string_get_cstr(temp_str));
if(subghz->protocol_result == NULL) {
subghz->txrx->protocol_result =
subghz_protocol_get_by_name(subghz->txrx->protocol, string_get_cstr(temp_str));
if(subghz->txrx->protocol_result == NULL) {
break;
}
if(!subghz->protocol_result->to_load_protocol_from_file(
file_worker, subghz->protocol_result)) {
if(!subghz->txrx->protocol_result->to_load_protocol_from_file(
file_worker, subghz->txrx->protocol_result)) {
break;
}
loaded = true;
@ -175,8 +187,9 @@ bool subghz_key_load(SubGhz* subghz, const char* file_path) {
}
bool subghz_save_protocol_to_file(void* context, const char* dev_name) {
furi_assert(context);
SubGhz* subghz = context;
furi_assert(subghz->protocol_result);
furi_assert(subghz->txrx->protocol_result);
FileWorker* file_worker = file_worker_alloc(false);
string_t dev_file_name;
string_init(dev_file_name);
@ -210,7 +223,7 @@ bool subghz_save_protocol_to_file(void* context, const char* dev_name) {
break;
}
//Get string save
subghz->protocol_result->to_save_string(subghz->protocol_result, temp_str);
subghz->txrx->protocol_result->to_save_string(subghz->txrx->protocol_result, temp_str);
// Prepare and write data to file
if(!file_worker_write(file_worker, string_get_cstr(temp_str), string_size(temp_str))) {
break;
@ -276,7 +289,7 @@ bool subghz_load_protocol_from_file(SubGhz* subghz) {
if(sscanf_res != 1) {
break;
}
subghz->frequency = (uint32_t)data;
subghz->txrx->frequency = (uint32_t)data;
// Read and parse preset from 2st line
if(!file_worker_read_until(file_worker, temp_str, '\n')) {
@ -286,7 +299,7 @@ bool subghz_load_protocol_from_file(SubGhz* subghz) {
if(sscanf_res != 1) {
break;
}
subghz->preset = (FuriHalSubGhzPreset)data;
subghz->txrx->preset = (FuriHalSubGhzPreset)data;
// Read and parse name protocol from 3st line
if(!file_worker_read_until(file_worker, temp_str, '\n')) {
@ -294,13 +307,13 @@ bool subghz_load_protocol_from_file(SubGhz* subghz) {
}
// strlen("Protocol: ") = 10
string_right(temp_str, 10);
subghz->protocol_result =
subghz_protocol_get_by_name(subghz->protocol, string_get_cstr(temp_str));
if(subghz->protocol_result == NULL) {
subghz->txrx->protocol_result =
subghz_protocol_get_by_name(subghz->txrx->protocol, string_get_cstr(temp_str));
if(subghz->txrx->protocol_result == NULL) {
break;
}
if(!subghz->protocol_result->to_load_protocol_from_file(
file_worker, subghz->protocol_result)) {
if(!subghz->txrx->protocol_result->to_load_protocol_from_file(
file_worker, subghz->txrx->protocol_result)) {
break;
}
res = true;
@ -328,3 +341,57 @@ uint32_t subghz_random_serial(void) {
}
return (uint32_t)rand();
}
void subghz_hopper_update(void* context) {
furi_assert(context);
SubGhzTxRx* txrx = context;
switch(txrx->hopper_state) {
case SubGhzHopperStateOFF:
return;
break;
case SubGhzHopperStatePause:
return;
break;
case SubGhzHopperStateRSSITimeOut:
if(txrx->hopper_timeout != 0) {
txrx->hopper_timeout--;
return;
}
break;
default:
break;
}
float rssi = -127.0f;
if(txrx->hopper_state != SubGhzHopperStateRSSITimeOut) {
// See RSSI Calculation timings in CC1101 17.3 RSSI
rssi = furi_hal_subghz_get_rssi();
// Stay if RSSI is high enough
if(rssi > -90.0f) {
txrx->hopper_timeout = 10;
txrx->hopper_state = SubGhzHopperStateRSSITimeOut;
return;
}
} else {
txrx->hopper_state = SubGhzHopperStateRunnig;
}
// Select next frequency
if(txrx->hopper_idx_frequency < subghz_hopper_frequencies_count - 1) {
txrx->hopper_idx_frequency++;
} else {
txrx->hopper_idx_frequency = 0;
}
if(txrx->txrx_state == SubGhzTxRxStateRx) {
subghz_rx_end(txrx->worker);
txrx->txrx_state = SubGhzTxRxStateIdle;
};
if(txrx->txrx_state == SubGhzTxRxStateIdle) {
subghz_protocol_reset(txrx->protocol);
txrx->frequency = subghz_hopper_frequencies[txrx->hopper_idx_frequency];
subghz_rx(txrx->worker, txrx->frequency);
txrx->txrx_state = SubGhzTxRxStateRx;
}
}

View File

@ -15,9 +15,9 @@
#include <notification/notification-messages.h>
#include <gui/view_dispatcher.h>
#include <gui/modules/submenu.h>
#include <gui/modules/dialog_ex.h>
#include <gui/modules/popup.h>
#include <gui/modules/text_input.h>
#include <gui/modules/widget.h>
#include <subghz/scenes/subghz_scene.h>
@ -26,6 +26,8 @@
#include <lib/subghz/protocols/subghz_protocol_common.h>
#include "subghz_history.h"
#include <gui/modules/variable-item-list.h>
#define SUBGHZ_TEXT_STORE_SIZE 128
#define NOTIFICATION_STARTING_STATE 0u
@ -33,48 +35,81 @@
#define NOTIFICATION_TX_STATE 2u
#define NOTIFICATION_RX_STATE 3u
extern const char* const subghz_frequencies_text[];
extern const uint32_t subghz_frequencies[];
extern const uint32_t subghz_hopper_frequencies[];
extern const uint32_t subghz_frequencies_count;
extern const uint32_t subghz_hopper_frequencies_count;
extern const uint32_t subghz_frequencies_433_92;
struct SubGhz {
Gui* gui;
NotificationApp* notifications;
/** SubGhzTxRx state */
typedef enum {
SubGhzTxRxStateIdle,
SubGhzTxRxStateRx,
SubGhzTxRxStateTx,
} SubGhzTxRxState;
/** SubGhzHopperState state */
typedef enum {
SubGhzHopperStateOFF,
SubGhzHopperStateRunnig,
SubGhzHopperStatePause,
SubGhzHopperStateRSSITimeOut,
} SubGhzHopperState;
struct SubGhzTxRx {
SubGhzWorker* worker;
SubGhzProtocol* protocol;
SubGhzProtocolCommon* protocol_result;
SubGhzProtocolCommonEncoder* encoder;
uint32_t frequency;
FuriHalSubGhzPreset preset;
SubGhzHistory* history;
uint16_t idx_menu_chosen;
SubGhzTxRxState txrx_state;
//bool hopper_runing;
SubGhzHopperState hopper_state;
uint8_t hopper_timeout;
uint8_t hopper_idx_frequency;
};
typedef struct SubGhzTxRx SubGhzTxRx;
struct SubGhz {
Gui* gui;
NotificationApp* notifications;
SubGhzTxRx* txrx;
SceneManager* scene_manager;
ViewDispatcher* view_dispatcher;
Submenu* submenu;
DialogEx* dialog_ex;
Popup* popup;
TextInput* text_input;
Widget* widget;
char text_store[SUBGHZ_TEXT_STORE_SIZE + 1];
uint8_t state_notifications;
SubghzReceiver* subghz_receiver;
SubghzTransmitter* subghz_transmitter;
VariableItemList* variable_item_list;
SubghzTestStatic* subghz_test_static;
SubghzTestCarrier* subghz_test_carrier;
SubghzTestPacket* subghz_test_packet;
string_t error_str;
};
typedef enum {
SubGhzViewMenu,
SubGhzViewDialogEx,
SubGhzViewReceiver,
SubGhzViewPopup,
SubGhzViewTextInput,
SubGhzViewWidget,
SubGhzViewTransmitter,
SubGhzViewVariableItemList,
SubGhzViewStatic,
SubGhzViewTestCarrier,
SubGhzViewTestPacket,
@ -86,8 +121,8 @@ uint32_t subghz_tx(uint32_t frequency);
void subghz_idle(void);
void subghz_rx_end(void* context);
void subghz_sleep(void);
void subghz_transmitter_tx_start(void* context);
void subghz_transmitter_tx_stop(void* context);
void subghz_tx_start(void* context);
void subghz_tx_stop(void* context);
bool subghz_key_load(SubGhz* subghz, const char* file_path);
bool subghz_save_protocol_to_file(void* context, const char* dev_name);
bool subghz_load_protocol_from_file(SubGhz* subghz);

View File

@ -1,42 +1,32 @@
#include "subghz_receiver.h"
#include "../subghz_i.h"
#include <math.h>
#include <furi.h>
#include <furi-hal.h>
#include <input/input.h>
#include <gui/elements.h>
#include <notification/notification-messages.h>
#include <lib/subghz/protocols/subghz_protocol_princeton.h>
#include <assets_icons.h>
#include <m-string.h>
#include <m-array.h>
#define FRAME_HEIGHT 12
#define MAX_LEN_PX 100
#define MENU_ITEMS 4
#define COUNT_FREQUNCY_HOPPER 3
const uint32_t subghz_frequencies_hopper[] = {
/* 300 - 348 */
315000000,
/* 387 - 464 */
433920000, /* LPD433 mid */
/* 779 - 928 */
868350000,
typedef struct {
string_t item_str;
uint8_t type;
} SubGhzReceiverMenuItem;
ARRAY_DEF(SubGhzReceiverMenuItemArray, SubGhzReceiverMenuItem, M_POD_OPLIST)
#define M_OPL_SubGhzReceiverMenuItemArray_t() \
ARRAY_OPLIST(SubGhzReceiverMenuItemArray, M_POD_OPLIST)
struct SubGhzReceiverHistory {
SubGhzReceiverMenuItemArray_t data;
};
typedef enum {
ReceiverSceneStart,
ReceiverSceneMain,
ReceiverSceneConfig,
ReceiverSceneInfo,
} SubghzReceiverScene;
typedef enum {
SubGhzHopperStateOFF,
SubGhzHopperStatePause,
SubGhzHopperStateRunnig,
SubGhzHopperStateRSSITimeOut,
} SubGhzHopperState;
typedef struct SubGhzReceiverHistory SubGhzReceiverHistory;
static const Icon* ReceiverItemIcons[] = {
[TYPE_PROTOCOL_UNKNOWN] = &I_Quest_7x8,
@ -48,27 +38,16 @@ struct SubghzReceiver {
View* view;
SubghzReceiverCallback callback;
void* context;
SubGhzWorker* worker;
SubGhzProtocol* protocol;
osTimerId timer;
SubGhzHopperState hopper_state;
uint8_t hopper_timeout;
uint32_t event_key_sequence;
};
typedef struct {
string_t text;
uint16_t scene;
SubGhzProtocolCommon* protocol_result;
SubGhzHistory* history;
uint8_t frequency;
uint8_t temp_frequency;
uint32_t real_frequency;
string_t frequency_str;
string_t preset_str;
string_t history_stat_str;
SubGhzReceiverHistory* history;
uint16_t idx;
uint16_t list_offset;
uint16_t history_item;
bool menu;
} SubghzReceiverModel;
void subghz_receiver_set_callback(
@ -81,35 +60,6 @@ void subghz_receiver_set_callback(
subghz_receiver->context = context;
}
void subghz_receiver_set_protocol(
SubghzReceiver* subghz_receiver,
SubGhzProtocolCommon* protocol_result,
SubGhzProtocol* protocol) {
furi_assert(subghz_receiver);
with_view_model(
subghz_receiver->view, (SubghzReceiverModel * model) {
model->protocol_result = protocol_result;
return true;
});
subghz_receiver->protocol = protocol;
}
SubGhzProtocolCommon* subghz_receiver_get_protocol(SubghzReceiver* subghz_receiver) {
furi_assert(subghz_receiver);
SubGhzProtocolCommon* result = NULL;
with_view_model(
subghz_receiver->view, (SubghzReceiverModel * model) {
result = model->protocol_result;
return false;
});
return result;
}
void subghz_receiver_set_worker(SubghzReceiver* subghz_receiver, SubGhzWorker* worker) {
furi_assert(subghz_receiver);
subghz_receiver->worker = worker;
}
static void subghz_receiver_update_offset(SubghzReceiver* subghz_receiver) {
furi_assert(subghz_receiver);
@ -129,6 +79,38 @@ static void subghz_receiver_update_offset(SubghzReceiver* subghz_receiver) {
});
}
void subghz_receiver_add_item_to_menu(
SubghzReceiver* subghz_receiver,
const char* name,
uint8_t type) {
furi_assert(subghz_receiver);
with_view_model(
subghz_receiver->view, (SubghzReceiverModel * model) {
SubGhzReceiverMenuItem* item_menu =
SubGhzReceiverMenuItemArray_push_raw(model->history->data);
string_init_set_str(item_menu->item_str, name);
item_menu->type = type;
model->history_item++;
return true;
});
subghz_receiver_update_offset(subghz_receiver);
}
void subghz_receiver_add_data_statusbar(
SubghzReceiver* subghz_receiver,
const char* frequency_str,
const char* preset_str,
const char* history_stat_str) {
furi_assert(subghz_receiver);
with_view_model(
subghz_receiver->view, (SubghzReceiverModel * model) {
string_set(model->frequency_str, frequency_str);
string_set(model->preset_str, preset_str);
string_set(model->history_stat_str, history_stat_str);
return true;
});
}
static void subghz_receiver_draw_frame(Canvas* canvas, uint16_t idx, bool scrollbar) {
canvas_set_color(canvas, ColorBlack);
canvas_draw_box(canvas, 0, 0 + idx * FRAME_HEIGHT, scrollbar ? 122 : 127, FRAME_HEIGHT);
@ -144,462 +126,113 @@ static void subghz_receiver_draw_frame(Canvas* canvas, uint16_t idx, bool scroll
}
void subghz_receiver_draw(Canvas* canvas, SubghzReceiverModel* model) {
bool scrollbar = model->history_item > 4;
string_t str_buff;
char buffer[64];
uint32_t frequency;
string_init(str_buff);
canvas_clear(canvas);
canvas_set_color(canvas, ColorBlack);
canvas_set_font(canvas, FontSecondary);
switch(model->scene) {
case ReceiverSceneMain:
for(size_t i = 0; i < MIN(model->history_item, MENU_ITEMS); ++i) {
size_t idx = CLAMP(i + model->list_offset, model->history_item, 0);
subghz_history_get_text_item_menu(model->history, str_buff, idx);
elements_string_fit_width(canvas, str_buff, scrollbar ? MAX_LEN_PX - 6 : MAX_LEN_PX);
if(model->idx == idx) {
subghz_receiver_draw_frame(canvas, i, scrollbar);
} else {
canvas_set_color(canvas, ColorBlack);
}
canvas_draw_icon(
canvas,
1,
2 + i * FRAME_HEIGHT,
ReceiverItemIcons[subghz_history_get_type_protocol(model->history, idx)]);
canvas_draw_str(canvas, 15, 9 + i * FRAME_HEIGHT, string_get_cstr(str_buff));
string_clean(str_buff);
}
if(scrollbar) {
elements_scrollbar_pos(canvas, 128, 0, 49, model->idx, model->history_item);
}
canvas_set_color(canvas, ColorBlack);
canvas_set_font(canvas, FontSecondary);
elements_button_left(canvas, "Config");
elements_button_left(canvas, "Config");
canvas_draw_line(canvas, 46, 51, 125, 51);
if(subghz_history_get_text_space_left(model->history, str_buff)) {
canvas_draw_str(canvas, 54, 62, string_get_cstr(str_buff));
} else {
if((model->real_frequency / 1000 % 10) > 4) {
frequency = model->real_frequency + 10000;
} else {
frequency = model->real_frequency;
}
snprintf(
buffer,
sizeof(buffer),
"%03ld.%02ld",
frequency / 1000000 % 1000,
frequency / 10000 % 100);
canvas_draw_str(canvas, 44, 62, buffer);
canvas_draw_str(canvas, 79, 62, "AM");
canvas_draw_str(canvas, 96, 62, string_get_cstr(str_buff));
}
break;
case ReceiverSceneStart:
canvas_draw_str(canvas, 44, 62, string_get_cstr(model->frequency_str));
canvas_draw_str(canvas, 79, 62, string_get_cstr(model->preset_str));
canvas_draw_str(canvas, 96, 62, string_get_cstr(model->history_stat_str));
if(model->history_item == 0) {
canvas_draw_icon(canvas, 0, 0, &I_Scanning_123x52);
canvas_set_font(canvas, FontPrimary);
canvas_draw_str(canvas, 63, 46, "Scanning...");
canvas_set_color(canvas, ColorBlack);
canvas_set_font(canvas, FontSecondary);
elements_button_left(canvas, "Config");
if((model->real_frequency / 1000 % 10) > 4) {
frequency = model->real_frequency + 10000;
} else {
frequency = model->real_frequency;
}
snprintf(
buffer,
sizeof(buffer),
"%03ld.%02ld",
frequency / 1000000 % 1000,
frequency / 10000 % 100);
canvas_draw_str(canvas, 44, 62, buffer);
canvas_draw_str(canvas, 79, 62, "AM");
subghz_history_get_text_space_left(model->history, str_buff);
canvas_draw_str(canvas, 96, 62, string_get_cstr(str_buff));
canvas_draw_line(canvas, 46, 51, 125, 51);
break;
case ReceiverSceneConfig:
if(model->frequency < subghz_frequencies_count) {
snprintf(
buffer,
sizeof(buffer),
"Frequency: < %03ld.%03ldMHz >",
model->real_frequency / 1000000 % 1000,
model->real_frequency / 1000 % 1000);
canvas_draw_str(canvas, 0, 8, buffer);
canvas_draw_str(canvas, 0, 18, "Frequency Hopping: <OFF>");
} else {
canvas_draw_str(canvas, 0, 8, "Frequency: < --- >");
canvas_draw_str(canvas, 0, 18, "Frequency Hopping: <ON>");
}
canvas_draw_str(canvas, 0, 28, "Modulation: <AM>");
elements_button_center(canvas, "Save");
break;
case ReceiverSceneInfo:
canvas_set_font(canvas, FontSecondary);
elements_multiline_text(canvas, 0, 8, string_get_cstr(model->text));
snprintf(
buffer,
sizeof(buffer),
"%03ld.%03ld",
subghz_history_get_frequency(model->history, model->idx) / 1000000 % 1000,
subghz_history_get_frequency(model->history, model->idx) / 1000 % 1000);
canvas_draw_str(canvas, 90, 8, buffer);
if(model->protocol_result && model->protocol_result->to_save_string &&
strcmp(model->protocol_result->name, "KeeLoq")) {
elements_button_right(canvas, "Save");
elements_button_center(canvas, "Send");
}
break;
default:
break;
return;
}
canvas_draw_line(canvas, 46, 51, 125, 51);
bool scrollbar = model->history_item > 4;
string_t str_buff;
string_init(str_buff);
SubGhzReceiverMenuItem* item_menu;
for(size_t i = 0; i < MIN(model->history_item, MENU_ITEMS); ++i) {
size_t idx = CLAMP(i + model->list_offset, model->history_item, 0);
item_menu = SubGhzReceiverMenuItemArray_get(model->history->data, idx);
string_set(str_buff, item_menu->item_str);
elements_string_fit_width(canvas, str_buff, scrollbar ? MAX_LEN_PX - 6 : MAX_LEN_PX);
if(model->idx == idx) {
subghz_receiver_draw_frame(canvas, i, scrollbar);
} else {
canvas_set_color(canvas, ColorBlack);
}
canvas_draw_icon(canvas, 1, 2 + i * FRAME_HEIGHT, ReceiverItemIcons[item_menu->type]);
canvas_draw_str(canvas, 15, 9 + i * FRAME_HEIGHT, string_get_cstr(str_buff));
string_clean(str_buff);
}
if(scrollbar) {
elements_scrollbar_pos(canvas, 128, 0, 49, model->idx, model->history_item);
}
string_clear(str_buff);
}
void subghz_receiver_history_full(void* context) {
furi_assert(context);
SubghzReceiver* subghz_receiver = context;
subghz_receiver->callback(SubghzReceverEventSendHistoryFull, subghz_receiver->context);
subghz_receiver->hopper_state = SubGhzHopperStateOFF;
}
bool subghz_receiver_input(InputEvent* event, void* context) {
furi_assert(context);
uint8_t scene = 0;
SubghzReceiver* subghz_receiver = context;
with_view_model(
subghz_receiver->view, (SubghzReceiverModel * model) {
scene = model->scene;
return false;
});
bool can_be_saved = false;
switch(scene) {
case ReceiverSceneMain:
if(event->key == InputKeyBack && event->type == InputTypeShort) {
with_view_model(
subghz_receiver->view, (SubghzReceiverModel * model) {
model->idx = 0;
model->list_offset = 0;
model->history_item = 0;
subghz_history_clean(model->history);
return true;
});
return false;
} else if(
event->key == InputKeyUp &&
(event->type == InputTypeShort || event->type == InputTypeRepeat)) {
with_view_model(
subghz_receiver->view, (SubghzReceiverModel * model) {
if(model->idx != 0) model->idx--;
return true;
});
} else if(
event->key == InputKeyDown &&
(event->type == InputTypeShort || event->type == InputTypeRepeat)) {
with_view_model(
subghz_receiver->view, (SubghzReceiverModel * model) {
if(model->idx != subghz_history_get_item(model->history) - 1) model->idx++;
return true;
});
} else if(event->key == InputKeyLeft && event->type == InputTypeShort) {
subghz_receiver->hopper_state = SubGhzHopperStatePause;
with_view_model(
subghz_receiver->view, (SubghzReceiverModel * model) {
model->scene = ReceiverSceneConfig;
model->temp_frequency = model->frequency;
return true;
});
subghz_receiver->callback(SubghzReceverEventConfig, subghz_receiver->context);
} else if(event->key == InputKeyOk && event->type == InputTypeShort) {
subghz_receiver->event_key_sequence = event->sequence;
with_view_model(
subghz_receiver->view, (SubghzReceiverModel * model) {
string_clean(model->text);
model->protocol_result = subghz_protocol_get_by_name(
subghz_receiver->protocol,
subghz_history_get_name(model->history, model->idx));
if(model->protocol_result->to_load_protocol != NULL) {
model->protocol_result->to_load_protocol(
model->protocol_result,
subghz_history_get_raw_data(model->history, model->idx));
model->protocol_result->to_string(model->protocol_result, model->text);
model->scene = ReceiverSceneInfo;
}
return true;
});
}
break;
case ReceiverSceneInfo:
if(event->key == InputKeyBack && event->type == InputTypeShort) {
subghz_receiver->callback(SubghzReceverEventBack, subghz_receiver->context);
} else if(
event->key == InputKeyUp &&
(event->type == InputTypeShort || event->type == InputTypeRepeat)) {
with_view_model(
subghz_receiver->view, (SubghzReceiverModel * model) {
can_be_saved =
(model->protocol_result && model->protocol_result->to_save_string &&
strcmp(model->protocol_result->name, "KeeLoq"));
return false;
if(model->idx != 0) model->idx--;
return true;
});
if(event->key == InputKeyBack && event->type == InputTypeShort) {
with_view_model(
subghz_receiver->view, (SubghzReceiverModel * model) {
subghz_rx_end(subghz_receiver->worker);
model->real_frequency =
subghz_rx(subghz_receiver->worker, subghz_frequencies[model->frequency]);
subghz_receiver->hopper_state = SubGhzHopperStateRunnig;
model->scene = ReceiverSceneMain;
return true;
});
subghz_receiver->callback(SubghzReceverEventMain, subghz_receiver->context);
} else if(can_be_saved && event->key == InputKeyRight) {
subghz_receiver->callback(SubghzReceverEventSave, subghz_receiver->context);
return false;
} else if(
can_be_saved && event->key == InputKeyOk && event->type == InputTypePress &&
subghz_receiver->event_key_sequence != event->sequence) {
subghz_receiver->hopper_state = SubGhzHopperStatePause;
subghz_rx_end(subghz_receiver->worker);
subghz_receiver->callback(SubghzReceverEventSendStart, subghz_receiver->context);
return true;
} else if(
can_be_saved && event->key == InputKeyOk && event->type == InputTypeRelease &&
subghz_receiver->event_key_sequence != event->sequence) {
subghz_receiver->callback(SubghzReceverEventSendStop, subghz_receiver->context);
return true;
}
break;
case ReceiverSceneConfig:
if(event->type != InputTypeShort) return false;
if(event->key == InputKeyBack) {
with_view_model(
subghz_receiver->view, (SubghzReceiverModel * model) {
model->frequency = model->temp_frequency;
model->real_frequency = subghz_frequencies[model->frequency];
subghz_receiver->hopper_state = SubGhzHopperStateRunnig;
if(subghz_history_get_item(model->history) == 0) {
model->scene = ReceiverSceneStart;
} else {
model->scene = ReceiverSceneMain;
}
return true;
});
subghz_receiver->callback(SubghzReceverEventMain, subghz_receiver->context);
} else if(event->key == InputKeyOk) {
with_view_model(
subghz_receiver->view, (SubghzReceiverModel * model) {
if(model->frequency < subghz_frequencies_count) {
subghz_rx_end(subghz_receiver->worker);
model->real_frequency = subghz_rx(
subghz_receiver->worker, subghz_frequencies[model->frequency]);
subghz_receiver->hopper_state = SubGhzHopperStateOFF;
} else {
osTimerStart(subghz_receiver->timer, 1024 / 10);
subghz_receiver->hopper_state = SubGhzHopperStateRunnig;
}
if(subghz_history_get_item(model->history) == 0) {
model->scene = ReceiverSceneStart;
} else {
model->scene = ReceiverSceneMain;
}
return true;
});
subghz_receiver->callback(SubghzReceverEventMain, subghz_receiver->context);
} else {
with_view_model(
subghz_receiver->view, (SubghzReceiverModel * model) {
bool model_updated = false;
if(event->key == InputKeyLeft) {
if(model->frequency > 0) model->frequency--;
model_updated = true;
} else if(event->key == InputKeyRight) {
if(model->frequency < subghz_frequencies_count) model->frequency++;
model_updated = true;
}
if(model_updated) {
model->real_frequency = subghz_frequencies[model->frequency];
}
return model_updated;
});
}
break;
case ReceiverSceneStart:
if(event->type != InputTypeShort) return false;
if(event->key == InputKeyBack) {
return false;
} else if(event->key == InputKeyLeft) {
subghz_receiver->hopper_state = SubGhzHopperStatePause;
with_view_model(
subghz_receiver->view, (SubghzReceiverModel * model) {
model->temp_frequency = model->frequency;
model->scene = ReceiverSceneConfig;
return true;
});
subghz_receiver->callback(SubghzReceverEventConfig, subghz_receiver->context);
}
break;
default:
break;
}
subghz_receiver_update_offset(subghz_receiver);
if(scene != ReceiverSceneInfo) {
} else if(
event->key == InputKeyDown &&
(event->type == InputTypeShort || event->type == InputTypeRepeat)) {
with_view_model(
subghz_receiver->view, (SubghzReceiverModel * model) {
if(subghz_history_get_text_space_left(model->history, NULL)) {
subghz_receiver_history_full(subghz_receiver);
if(model->idx != model->history_item - 1) model->idx++;
return true;
});
} else if(event->key == InputKeyLeft && event->type == InputTypeShort) {
subghz_receiver->callback(SubghzReceverEventConfig, subghz_receiver->context);
} else if(event->key == InputKeyOk && event->type == InputTypeShort) {
with_view_model(
subghz_receiver->view, (SubghzReceiverModel * model) {
if(model->history_item != 0) {
subghz_receiver->callback(SubghzReceverEventOK, subghz_receiver->context);
}
return false;
});
}
subghz_receiver_update_offset(subghz_receiver);
return true;
}
void subghz_receiver_text_callback(string_t text, void* context) {
furi_assert(context);
SubghzReceiver* subghz_receiver = context;
with_view_model(
subghz_receiver->view, (SubghzReceiverModel * model) {
string_set(model->text, text);
model->scene = ReceiverSceneMain;
return true;
});
}
void subghz_receiver_protocol_callback(SubGhzProtocolCommon* parser, void* context) {
furi_assert(context);
SubghzReceiver* subghz_receiver = context;
with_view_model(
subghz_receiver->view, (SubghzReceiverModel * model) {
model->protocol_result = parser;
subghz_history_set_frequency_preset(
model->history,
model->history_item,
model->real_frequency,
FuriHalSubGhzPresetOok650Async);
subghz_history_add_to_history(model->history, parser);
model->history_item = subghz_history_get_item(model->history);
model->scene = ReceiverSceneMain;
if(subghz_history_get_text_space_left(model->history, NULL)) {
subghz_receiver_history_full(subghz_receiver);
}
return true;
});
subghz_protocol_reset(subghz_receiver->protocol);
subghz_receiver_update_offset(subghz_receiver);
}
static void subghz_receiver_timer_callback(void* context) {
furi_assert(context);
SubghzReceiver* subghz_receiver = context;
switch(subghz_receiver->hopper_state) {
case SubGhzHopperStatePause:
return;
break;
case SubGhzHopperStateOFF:
osTimerStop(subghz_receiver->timer);
return;
break;
case SubGhzHopperStateRSSITimeOut:
if(subghz_receiver->hopper_timeout != 0) {
subghz_receiver->hopper_timeout--;
return;
}
break;
default:
break;
}
float rssi = -127.0f;
with_view_model(
subghz_receiver->view, (SubghzReceiverModel * model) {
if(subghz_receiver->hopper_state != SubGhzHopperStateRSSITimeOut) {
// See RSSI Calculation timings in CC1101 17.3 RSSI
rssi = furi_hal_subghz_get_rssi();
// Stay if RSSI is high enough
if(rssi > -90.0f) {
subghz_receiver->hopper_timeout = 10;
subghz_receiver->hopper_state = SubGhzHopperStateRSSITimeOut;
return false;
}
} else {
subghz_receiver->hopper_state = SubGhzHopperStateRunnig;
}
// Select next frequency
if(model->frequency < COUNT_FREQUNCY_HOPPER - 1) {
model->frequency++;
} else {
model->frequency = 0;
}
// Restart radio
furi_hal_subghz_idle();
subghz_protocol_reset(subghz_receiver->protocol);
model->real_frequency = furi_hal_subghz_set_frequency_and_path(
subghz_frequencies_hopper[model->frequency]);
furi_hal_subghz_rx();
return true;
});
}
void subghz_receiver_enter(void* context) {
furi_assert(context);
SubghzReceiver* subghz_receiver = context;
//Start CC1101 Rx
subghz_begin(FuriHalSubGhzPresetOok650Async);
with_view_model(
subghz_receiver->view, (SubghzReceiverModel * model) {
subghz_rx_end(subghz_receiver->worker);
model->frequency = subghz_frequencies_433_92;
model->real_frequency =
subghz_rx(subghz_receiver->worker, subghz_frequencies[model->frequency]);
if(subghz_history_get_item(model->history) == 0) {
model->scene = ReceiverSceneStart;
} else {
model->scene = ReceiverSceneMain;
}
return true;
});
subghz_protocol_enable_dump(
subghz_receiver->protocol, subghz_receiver_protocol_callback, subghz_receiver);
//SubghzReceiver* subghz_receiver = context;
}
void subghz_receiver_exit(void* context) {
furi_assert(context);
SubghzReceiver* subghz_receiver = context;
osTimerStop(subghz_receiver->timer);
with_view_model(
subghz_receiver->view, (SubghzReceiverModel * model) {
string_clean(model->text);
return true;
string_clean(model->frequency_str);
string_clean(model->preset_str);
string_clean(model->history_stat_str);
for
M_EACH(item_menu, model->history->data, SubGhzReceiverMenuItemArray_t) {
string_clear(item_menu->item_str);
item_menu->type = 0;
}
SubGhzReceiverMenuItemArray_clean(model->history->data);
model->idx = 0;
model->list_offset = 0;
model->history_item = 0;
return false;
});
// Stop CC1101 Rx
subghz_rx_end(subghz_receiver->worker);
subghz_sleep();
}
SubghzReceiver* subghz_receiver_alloc() {
@ -616,14 +249,14 @@ SubghzReceiver* subghz_receiver_alloc() {
with_view_model(
subghz_receiver->view, (SubghzReceiverModel * model) {
string_init(model->text);
model->history = subghz_history_alloc();
string_init(model->frequency_str);
string_init(model->preset_str);
string_init(model->history_stat_str);
model->history = furi_alloc(sizeof(SubGhzReceiverHistory));
SubGhzReceiverMenuItemArray_init(model->history->data);
return true;
});
subghz_receiver->timer =
osTimerNew(subghz_receiver_timer_callback, osTimerPeriodic, subghz_receiver, NULL);
subghz_receiver->hopper_state = SubGhzHopperStateOFF;
return subghz_receiver;
}
@ -632,11 +265,18 @@ void subghz_receiver_free(SubghzReceiver* subghz_receiver) {
with_view_model(
subghz_receiver->view, (SubghzReceiverModel * model) {
string_clear(model->text);
subghz_history_free(model->history);
return false;
string_clear(model->frequency_str);
string_clear(model->preset_str);
string_clear(model->history_stat_str);
for
M_EACH(item_menu, model->history->data, SubGhzReceiverMenuItemArray_t) {
string_clear(item_menu->item_str);
item_menu->type = 0;
}
SubGhzReceiverMenuItemArray_clear(model->history->data);
free(model->history);
return false;
});
osTimerDelete(subghz_receiver->timer);
view_free(subghz_receiver->view);
free(subghz_receiver);
}
@ -646,43 +286,24 @@ View* subghz_receiver_get_view(SubghzReceiver* subghz_receiver) {
return subghz_receiver->view;
}
uint32_t subghz_receiver_get_frequency(SubghzReceiver* subghz_receiver) {
uint16_t subghz_receiver_get_idx_menu(SubghzReceiver* subghz_receiver) {
furi_assert(subghz_receiver);
uint32_t frequency;
uint32_t idx = 0;
with_view_model(
subghz_receiver->view, (SubghzReceiverModel * model) {
frequency = subghz_history_get_frequency(model->history, model->idx);
idx = model->idx;
return false;
});
return frequency;
return idx;
}
FuriHalSubGhzPreset subghz_receiver_get_preset(SubghzReceiver* subghz_receiver) {
void subghz_receiver_set_idx_menu(SubghzReceiver* subghz_receiver, uint16_t idx) {
furi_assert(subghz_receiver);
FuriHalSubGhzPreset preset;
with_view_model(
subghz_receiver->view, (SubghzReceiverModel * model) {
preset = subghz_history_get_preset(model->history, model->idx);
return false;
model->idx = idx;
if(model->idx > 2) model->list_offset = idx - 2;
return true;
});
return preset;
}
void subghz_receiver_frequency_preset_to_str(SubghzReceiver* subghz_receiver, string_t output) {
furi_assert(subghz_receiver);
uint32_t frequency;
uint32_t preset;
with_view_model(
subghz_receiver->view, (SubghzReceiverModel * model) {
frequency = subghz_history_get_frequency(model->history, model->idx);
preset = (uint32_t)subghz_history_get_preset(model->history, model->idx);
return false;
});
string_cat_printf(
output,
"Frequency: %d\n"
"Preset: %d\n",
(int)frequency,
(int)preset);
}
subghz_receiver_update_offset(subghz_receiver);
}

View File

@ -1,21 +1,11 @@
#pragma once
#include <gui/view.h>
#include <lib/subghz/protocols/subghz_protocol_common.h>
#include <lib/subghz/protocols/subghz_protocol.h>
#include <lib/subghz/subghz_worker.h>
#include "../subghz_history.h"
typedef enum {
SubghzReceverEventOK,
SubghzReceverEventConfig,
SubghzReceverEventMain,
SubghzReceverEventSave,
SubghzReceverEventBack,
SubghzReceverEventMore,
SubghzReceverEventSendStart,
SubghzReceverEventSendStop,
SubghzReceverEventSendHistoryFull,
} SubghzReceverEvent;
typedef struct SubghzReceiver SubghzReceiver;
@ -33,17 +23,19 @@ void subghz_receiver_free(SubghzReceiver* subghz_receiver);
View* subghz_receiver_get_view(SubghzReceiver* subghz_receiver);
void subghz_receiver_set_protocol(
void subghz_receiver_add_data_statusbar(
SubghzReceiver* subghz_receiver,
SubGhzProtocolCommon* protocol_result,
SubGhzProtocol* protocol);
const char* frequency_str,
const char* preset_str,
const char* history_stat_str);
SubGhzProtocolCommon* subghz_receiver_get_protocol(SubghzReceiver* subghz_receiver);
void subghz_receiver_add_item_to_menu(
SubghzReceiver* subghz_receiver,
const char* name,
uint8_t type);
void subghz_receiver_set_worker(SubghzReceiver* subghz_receiver, SubGhzWorker* worker);
uint16_t subghz_receiver_get_idx_menu(SubghzReceiver* subghz_receiver);
uint32_t subghz_receiver_get_frequency(SubghzReceiver* subghz_receiver);
void subghz_receiver_set_idx_menu(SubghzReceiver* subghz_receiver, uint16_t idx);
FuriHalSubGhzPreset subghz_receiver_get_preset(SubghzReceiver* subghz_receiver);
void subghz_receiver_frequency_preset_to_str(SubghzReceiver* subghz_receiver, string_t output);
void subghz_receiver_exit(void* context);

View File

@ -1,13 +1,8 @@
#include "subghz_transmitter.h"
#include "../subghz_i.h"
#include <math.h>
#include <furi.h>
#include <furi-hal.h>
#include <input/input.h>
#include <gui/elements.h>
#include <notification/notification-messages.h>
#include <lib/subghz/protocols/subghz_protocol_keeloq.h>
struct SubghzTransmitter {
View* view;
@ -16,11 +11,10 @@ struct SubghzTransmitter {
};
typedef struct {
string_t text;
uint16_t scene;
uint32_t real_frequency;
FuriHalSubGhzPreset preset;
SubGhzProtocolCommon* protocol;
string_t frequency_str;
string_t preset_str;
string_t key_str;
uint8_t show_button;
} SubghzTransmitterModel;
void subghz_transmitter_set_callback(
@ -33,24 +27,19 @@ void subghz_transmitter_set_callback(
subghz_transmitter->context = context;
}
void subghz_transmitter_set_protocol(
void subghz_transmitter_add_data_to_show(
SubghzTransmitter* subghz_transmitter,
SubGhzProtocolCommon* protocol) {
const char* key_str,
const char* frequency_str,
const char* preset_str,
uint8_t show_button) {
furi_assert(subghz_transmitter);
with_view_model(
subghz_transmitter->view, (SubghzTransmitterModel * model) {
model->protocol = protocol;
return true;
});
}
void subghz_transmitter_set_frequency_preset(
SubghzTransmitter* subghz_transmitter,
uint32_t frequency,
FuriHalSubGhzPreset preset) {
with_view_model(
subghz_transmitter->view, (SubghzTransmitterModel * model) {
model->real_frequency = frequency;
model->preset = preset;
string_set(model->key_str, key_str);
string_set(model->frequency_str, frequency_str);
string_set(model->preset_str, preset_str);
model->show_button = show_button;
return true;
});
}
@ -87,26 +76,13 @@ static void subghz_transmitter_button_right(Canvas* canvas, const char* str) {
}
void subghz_transmitter_draw(Canvas* canvas, SubghzTransmitterModel* model) {
char buffer[64];
canvas_clear(canvas);
canvas_set_color(canvas, ColorBlack);
canvas_set_font(canvas, FontSecondary);
elements_multiline_text(canvas, 0, 8, string_get_cstr(model->text));
snprintf(
buffer,
sizeof(buffer),
"%03ld.%03ld",
model->real_frequency / 1000000 % 1000,
model->real_frequency / 1000 % 1000);
canvas_draw_str(canvas, 90, 8, buffer);
if(model->protocol && model->protocol->get_upload_protocol) {
if((!strcmp(model->protocol->name, "KeeLoq")) &&
(!strcmp(subghz_protocol_keeloq_get_manufacture_name(model->protocol), "Unknown"))) {
return;
}
subghz_transmitter_button_right(canvas, "Send");
}
elements_multiline_text(canvas, 0, 8, string_get_cstr(model->key_str));
canvas_draw_str(canvas, 78, 8, string_get_cstr(model->frequency_str));
canvas_draw_str(canvas, 113, 8, string_get_cstr(model->preset_str));
if(model->show_button) subghz_transmitter_button_right(canvas, "Send");
}
bool subghz_transmitter_input(InputEvent* event, void* context) {
@ -114,26 +90,25 @@ bool subghz_transmitter_input(InputEvent* event, void* context) {
SubghzTransmitter* subghz_transmitter = context;
bool can_be_sent = false;
if(event->key == InputKeyBack) {
if(event->key == InputKeyBack && event->type == InputTypeShort) {
with_view_model(
subghz_transmitter->view, (SubghzTransmitterModel * model) {
string_clean(model->frequency_str);
string_clean(model->preset_str);
string_clean(model->key_str);
model->show_button = 0;
return false;
});
return false;
}
with_view_model(
subghz_transmitter->view, (SubghzTransmitterModel * model) {
if(model->protocol && model->protocol->get_upload_protocol) {
if((!strcmp(model->protocol->name, "KeeLoq")) &&
(!strcmp(
subghz_protocol_keeloq_get_manufacture_name(model->protocol), "Unknown"))) {
return false;
}
if(model->show_button) {
can_be_sent = true;
}
//can_be_sent = (model->protocol && model->protocol->get_upload_protocol);
string_clean(model->text);
model->protocol->to_string(model->protocol, model->text);
return true;
});
//if(event->type != InputTypeShort) return false;
if(can_be_sent && event->key == InputKeyOk && event->type == InputTypePress) {
subghz_transmitter->callback(SubghzTransmitterEventSendStart, subghz_transmitter->context);
@ -146,37 +121,14 @@ bool subghz_transmitter_input(InputEvent* event, void* context) {
return true;
}
void subghz_transmitter_text_callback(string_t text, void* context) {
furi_assert(context);
SubghzTransmitter* subghz_transmitter = context;
with_view_model(
subghz_transmitter->view, (SubghzTransmitterModel * model) {
string_set(model->text, text);
model->scene = 0;
return true;
});
}
void subghz_transmitter_enter(void* context) {
furi_assert(context);
SubghzTransmitter* subghz_transmitter = context;
with_view_model(
subghz_transmitter->view, (SubghzTransmitterModel * model) {
string_clean(model->text);
model->protocol->to_string(model->protocol, model->text);
return true;
});
// SubghzTransmitter* subghz_transmitter = context;
}
void subghz_transmitter_exit(void* context) {
furi_assert(context);
SubghzTransmitter* subghz_transmitter = context;
with_view_model(
subghz_transmitter->view, (SubghzTransmitterModel * model) {
string_clean(model->text);
return true;
});
// SubghzTransmitter* subghz_transmitter = context;
}
SubghzTransmitter* subghz_transmitter_alloc() {
@ -194,7 +146,9 @@ SubghzTransmitter* subghz_transmitter_alloc() {
with_view_model(
subghz_transmitter->view, (SubghzTransmitterModel * model) {
string_init(model->text);
string_init(model->frequency_str);
string_init(model->preset_str);
string_init(model->key_str);
return true;
});
return subghz_transmitter;
@ -205,7 +159,9 @@ void subghz_transmitter_free(SubghzTransmitter* subghz_transmitter) {
with_view_model(
subghz_transmitter->view, (SubghzTransmitterModel * model) {
string_clear(model->text);
string_clear(model->frequency_str);
string_clear(model->preset_str);
string_clear(model->key_str);
return true;
});
view_free(subghz_transmitter->view);

View File

@ -1,7 +1,6 @@
#pragma once
#include <gui/view.h>
#include <lib/subghz/protocols/subghz_protocol_common.h>
typedef enum {
SubghzTransmitterEventSendStart,
@ -25,10 +24,9 @@ void subghz_transmitter_free(SubghzTransmitter* subghz_transmitter);
View* subghz_transmitter_get_view(SubghzTransmitter* subghz_transmitter);
void subghz_transmitter_set_protocol(
void subghz_transmitter_add_data_to_show(
SubghzTransmitter* subghz_transmitter,
SubGhzProtocolCommon* protocol);
void subghz_transmitter_set_frequency_preset(
SubghzTransmitter* subghz_transmitter,
uint32_t frequency,
FuriHalSubGhzPreset preset);
const char* key_str,
const char* frequency_str,
const char* preset_str,
uint8_t show_button);

View File

@ -7,6 +7,7 @@
#include "test_data/irda_samsung_test_data.srcdata"
#include "test_data/irda_rc6_test_data.srcdata"
#include "test_data/irda_rc5_test_data.srcdata"
#include "test_data/irda_sirc_test_data.srcdata"
#define RUN_ENCODER(data, expected) \
run_encoder((data), COUNT_OF(data), (expected), COUNT_OF(expected))
@ -14,6 +15,8 @@
#define RUN_DECODER(data, expected) \
run_decoder((data), COUNT_OF(data), (expected), COUNT_OF(expected))
#define RUN_ENCODER_DECODER(data) run_encoder_decoder((data), COUNT_OF(data))
static IrdaDecoderHandler* decoder_handler;
static IrdaEncoderHandler* encoder_handler;
@ -33,27 +36,44 @@ static void compare_message_results(
mu_check(message_decoded->protocol == message_expected->protocol);
mu_check(message_decoded->command == message_expected->command);
mu_check(message_decoded->address == message_expected->address);
mu_check(message_decoded->repeat == message_expected->repeat);
if((message_expected->protocol == IrdaProtocolSIRC) ||
(message_expected->protocol == IrdaProtocolSIRC15) ||
(message_expected->protocol == IrdaProtocolSIRC20)) {
mu_check(message_decoded->repeat == false);
} else {
mu_check(message_decoded->repeat == message_expected->repeat);
}
}
static void
run_encoder_fill_array(IrdaEncoderHandler* handler, uint32_t* timings, uint32_t* timings_len) {
/* Encodes signal and merges same levels (high+high, low+low) */
static void run_encoder_fill_array(
IrdaEncoderHandler* handler,
uint32_t* timings,
uint32_t* timings_len,
bool* start_level) {
uint32_t duration = 0;
bool level = false; // start from space
bool level = false;
bool level_read;
IrdaStatus status = IrdaStatusError;
int i = 0;
bool first = true;
while(1) {
status = irda_encode(handler, &duration, &level_read);
if(level_read != level) {
level = level_read;
if(first) {
if(start_level) *start_level = level_read;
first = false;
timings[0] = 0;
} else if(level_read != level) {
++i;
furi_assert(i < *timings_len);
timings[i] = 0;
}
level = level_read;
timings[i] += duration;
furi_assert((status == IrdaStatusOk) || (status == IrdaStatusDone));
if(status == IrdaStatusDone) break;
furi_assert(i < *timings_len);
}
*timings_len = i + 1;
@ -66,8 +86,9 @@ static void run_encoder(
const uint32_t expected_timings[],
uint32_t expected_timings_len) {
uint32_t* timings = 0;
uint32_t timings_len = 0;
uint32_t timings_len = 200;
uint32_t j = 0;
timings = furi_alloc(sizeof(uint32_t) * timings_len);
for(uint32_t message_counter = 0; message_counter < input_messages_len; ++message_counter) {
const IrdaMessage* message = &input_messages[message_counter];
@ -76,44 +97,51 @@ static void run_encoder(
}
timings_len = 200;
timings = furi_alloc(sizeof(uint32_t) * timings_len);
run_encoder_fill_array(encoder_handler, timings, &timings_len);
run_encoder_fill_array(encoder_handler, timings, &timings_len, NULL);
furi_assert(timings_len <= 200);
for(int i = 0; i < timings_len; ++i, ++j) {
mu_check(MATCH_BIT_TIMING(timings[i], expected_timings[j], 120));
mu_check(MATCH_TIMING(timings[i], expected_timings[j], 120));
mu_assert(j < expected_timings_len, "encoded more timings than expected");
}
free(timings);
}
free(timings);
mu_assert(j == expected_timings_len, "encoded less timings than expected");
}
static void run_encoder_decoder(const IrdaMessage input_messages[], uint32_t input_messages_len) {
uint32_t* timings = 0;
uint32_t timings_len = 0;
uint32_t timings_len = 200;
bool level = false;
timings = furi_alloc(sizeof(uint32_t) * timings_len);
for(uint32_t message_counter = 0; message_counter < input_messages_len; ++message_counter) {
const IrdaMessage* message_encoded = &input_messages[message_counter];
if(!message_encoded->repeat) {
irda_reset_encoder(encoder_handler, message_encoded);
level = false;
}
timings_len = 200;
timings = furi_alloc(sizeof(uint32_t) * timings_len);
run_encoder_fill_array(encoder_handler, timings, &timings_len);
run_encoder_fill_array(encoder_handler, timings, &timings_len, &level);
furi_assert(timings_len <= 200);
const IrdaMessage* message_decoded = 0;
for(int i = 0; i < timings_len; ++i) {
message_decoded = irda_decode(decoder_handler, level, timings[i]);
if(i < timings_len - 1)
if((i == timings_len - 2) && level && message_decoded) {
/* In case we end with space timing - message can be decoded at last mark.
* Exception - SIRC protocol, which has variable message length (12/15/20),
* and decoder recognizes protocol by silence time before next message
* or by timeout (irda_check_decoder_ready()). */
break;
} else if(i < timings_len - 1) {
mu_check(!message_decoded);
else
} else {
if(!message_decoded) {
message_decoded = irda_check_decoder_ready(decoder_handler);
}
mu_check(message_decoded);
}
level = !level;
}
if(message_decoded) {
@ -121,9 +149,8 @@ static void run_encoder_decoder(const IrdaMessage input_messages[], uint32_t inp
} else {
mu_check(0);
}
free(timings);
}
free(timings);
}
static void run_decoder(
@ -131,21 +158,49 @@ static void run_decoder(
uint32_t input_delays_len,
const IrdaMessage* message_expected,
uint32_t message_expected_len) {
const IrdaMessage* message_decoded = 0;
IrdaMessage message_decoded_check_local;
bool level = 0;
uint32_t message_counter = 0;
const IrdaMessage* message_decoded = 0;
for(uint32_t i = 0; i < input_delays_len; ++i) {
const IrdaMessage* message_decoded_check = 0;
if(input_delays[i] > IRDA_RAW_RX_TIMING_DELAY_US) {
message_decoded_check = irda_check_decoder_ready(decoder_handler);
if(message_decoded_check) {
/* irda_decode() can reset message, but we have to call irda_decode() to perform real
* simulation: irda_check() by timeout, then irda_decode() when meet edge */
message_decoded_check_local = *message_decoded_check;
message_decoded_check = &message_decoded_check_local;
}
}
message_decoded = irda_decode(decoder_handler, level, input_delays[i]);
if(message_decoded) {
if(message_decoded_check || message_decoded) {
mu_assert(
!(message_decoded_check && message_decoded),
"both messages decoded: check_ready() and irda_decode()");
if(message_decoded_check) {
message_decoded = message_decoded_check;
}
mu_assert(message_counter < message_expected_len, "decoded more than expected");
if(message_counter >= message_expected_len) break;
compare_message_results(message_decoded, &message_expected[message_counter]);
++message_counter;
}
level = !level;
}
message_decoded = irda_check_decoder_ready(decoder_handler);
if(message_decoded) {
compare_message_results(message_decoded, &message_expected[message_counter]);
++message_counter;
}
mu_assert(message_counter == message_expected_len, "decoded less than expected");
}
@ -155,6 +210,7 @@ MU_TEST(test_decoder_samsung32) {
MU_TEST(test_mix) {
RUN_DECODER(test_decoder_rc5_input2, test_decoder_rc5_expected2);
RUN_DECODER(test_decoder_sirc_input1, test_decoder_sirc_expected1);
RUN_DECODER(test_decoder_necext_input1, test_decoder_necext_expected1);
// can use encoder data for decoding, but can't do opposite
RUN_DECODER(test_encoder_rc6_expected1, test_encoder_rc6_input1);
@ -162,12 +218,16 @@ MU_TEST(test_mix) {
RUN_DECODER(test_decoder_rc6_input1, test_decoder_rc6_expected1);
RUN_DECODER(test_decoder_samsung32_input1, test_decoder_samsung32_expected1);
RUN_DECODER(test_decoder_rc5_input1, test_decoder_rc5_expected1);
RUN_DECODER(test_decoder_sirc_input2, test_decoder_sirc_expected2);
RUN_DECODER(test_decoder_necext_input1, test_decoder_necext_expected1);
RUN_DECODER(test_decoder_sirc_input4, test_decoder_sirc_expected4);
RUN_DECODER(test_decoder_nec_input2, test_decoder_nec_expected2);
RUN_DECODER(test_decoder_rc6_input1, test_decoder_rc6_expected1);
RUN_DECODER(test_decoder_necext_input1, test_decoder_necext_expected1);
RUN_DECODER(test_decoder_sirc_input5, test_decoder_sirc_expected5);
RUN_DECODER(test_decoder_rc5_input5, test_decoder_rc5_expected5);
RUN_DECODER(test_decoder_samsung32_input1, test_decoder_samsung32_expected1);
RUN_DECODER(test_decoder_sirc_input3, test_decoder_sirc_expected3);
}
MU_TEST(test_decoder_nec1) {
@ -191,6 +251,20 @@ MU_TEST(test_decoder_necext1) {
RUN_DECODER(test_decoder_necext_input1, test_decoder_necext_expected1);
}
MU_TEST(test_encoder_sirc) {
RUN_ENCODER(test_encoder_sirc_input1, test_encoder_sirc_expected1);
RUN_ENCODER(test_encoder_sirc_input2, test_encoder_sirc_expected2);
}
MU_TEST(test_decoder_sirc) {
RUN_DECODER(test_decoder_sirc_input3, test_decoder_sirc_expected3);
RUN_DECODER(test_decoder_sirc_input1, test_decoder_sirc_expected1);
RUN_DECODER(test_decoder_sirc_input2, test_decoder_sirc_expected2);
RUN_DECODER(test_decoder_sirc_input4, test_decoder_sirc_expected4);
RUN_DECODER(test_decoder_sirc_input5, test_decoder_sirc_expected5);
RUN_ENCODER_DECODER(test_sirc);
}
MU_TEST(test_decoder_rc5) {
RUN_DECODER(test_decoder_rc5x_input1, test_decoder_rc5x_expected1);
RUN_DECODER(test_decoder_rc5_input1, test_decoder_rc5_expected1);
@ -219,16 +293,19 @@ MU_TEST(test_encoder_rc6) {
}
MU_TEST(test_encoder_decoder_all) {
run_encoder_decoder(test_nec_all, COUNT_OF(test_nec_all));
run_encoder_decoder(test_necext_all, COUNT_OF(test_necext_all));
run_encoder_decoder(test_samsung32_all, COUNT_OF(test_samsung32_all));
run_encoder_decoder(test_rc6_all, COUNT_OF(test_rc6_all));
run_encoder_decoder(test_rc5_all, COUNT_OF(test_rc5_all));
RUN_ENCODER_DECODER(test_nec);
RUN_ENCODER_DECODER(test_necext);
RUN_ENCODER_DECODER(test_samsung32);
RUN_ENCODER_DECODER(test_rc6);
RUN_ENCODER_DECODER(test_rc5);
RUN_ENCODER_DECODER(test_sirc);
}
MU_TEST_SUITE(test_irda_decoder_encoder) {
MU_SUITE_CONFIGURE(&test_setup, &test_teardown);
MU_RUN_TEST(test_encoder_sirc);
MU_RUN_TEST(test_decoder_sirc);
MU_RUN_TEST(test_encoder_rc5x);
MU_RUN_TEST(test_encoder_rc5);
MU_RUN_TEST(test_decoder_rc5);

View File

@ -178,7 +178,7 @@ const IrdaMessage test_decoder_nec_expected2[] = {
{IrdaProtocolNEC, 0x00, 0x0A, true},
};
const IrdaMessage test_nec_all[] = {
const IrdaMessage test_nec[] = {
{IrdaProtocolNEC, 0x00, 0x00, false},
{IrdaProtocolNEC, 0x01, 0x00, false},
{IrdaProtocolNEC, 0x01, 0x80, false},

View File

@ -223,7 +223,7 @@ const IrdaMessage test_decoder_necext_expected1[] = {
const IrdaMessage test_necext_all[] = {
const IrdaMessage test_necext[] = {
{IrdaProtocolNECext, 0x0000, 0x00, false},
{IrdaProtocolNECext, 0x0001, 0x00, false},
{IrdaProtocolNECext, 0x0001, 0x80, false},

View File

@ -128,7 +128,7 @@ const IrdaMessage test_decoder_rc5_expected_all_repeats[] = {
};
const IrdaMessage test_rc5_all[] = {
const IrdaMessage test_rc5[] = {
{IrdaProtocolRC5, 0x1F, 0x3F, false},
{IrdaProtocolRC5, 0x00, 0x00, false},
{IrdaProtocolRC5, 0x10, 0x01, false},

View File

@ -65,7 +65,7 @@ const uint32_t test_encoder_rc6_expected1[] = {
};
const IrdaMessage test_rc6_all[] = {
const IrdaMessage test_rc6[] = {
{IrdaProtocolRC6, 0x00, 0x00, false}, // t 0
{IrdaProtocolRC6, 0x80, 0x00, false}, // t 1
{IrdaProtocolRC6, 0x80, 0x01, false}, // t 0

View File

@ -221,7 +221,7 @@ const IrdaMessage test_decoder_samsung32_expected1[] = {
{IrdaProtocolSamsung32, 0x0E, 0x01, false}, {IrdaProtocolSamsung32, 0x0E, 0x01, true},
};
const IrdaMessage test_samsung32_all[] = {
const IrdaMessage test_samsung32[] = {
{IrdaProtocolSamsung32, 0x00, 0x00, false},
{IrdaProtocolSamsung32, 0x01, 0x00, false},
{IrdaProtocolSamsung32, 0x01, 0x80, false},

View File

@ -0,0 +1,485 @@
const uint32_t test_decoder_sirc_input1[] = { /* 121 timings */
1000000, 2420, 608, 1194, 608, 596, 604, 1198, 603, 591, 610, 1192, 609, 596, 605, 599, 601, 593, 607, 597, 604, 590, 610, 594, 606, 1196,
25957, 2426, 603, 1199, 603, 591, 610, 1192, 610, 594, 606, 1196, 606, 599, 603, 591, 609, 595, 606, 598, 602, 592, 609, 596, 605, 1197,
25960, 2423, 606, 1196, 606, 599, 602, 1200, 602, 592, 609, 1193, 609, 596, 606, 599, 602, 592, 609, 595, 605, 600, 601, 593, 608, 1194,
1000000, 2422, 607, 1195, 607, 598, 603, 1199, 604, 590, 610, 1192, 610, 594, 606, 598, 603, 591, 609, 595, 605, 600, 601, 593, 607, 1195,
25955, 2418, 610, 1192, 610, 594, 606, 1196, 606, 599, 602, 1200, 602, 592, 608, 596, 604, 590, 611, 594, 607, 597, 603, 591, 609, 1193,
25959, 2424, 604, 1198, 604, 590, 610, 1192, 610, 594, 606, 1196, 605, 600, 601, 593, 608, 597, 603, 591, 610, 595, 606, 598, 602, 1200,
1000000, 2424, 605, 599, 601, 593, 607, 597, 603, 591, 610, 594, 606, 1196, 606, 1196, 605, 600, 601, 593, 608, 597, 604, 590, 611, 1191,
26586, 2425, 604, 590, 611, 593, 607, 598, 603, 591, 610, 595, 606, 1196, 606, 1196, 606, 599, 602, 592, 608, 596, 604, 590, 611, 1191,
26586, 2424, 604, 590, 611, 593, 607, 598, 603, 591, 609, 595, 605, 1197, 605, 1197, 604, 590, 611, 593, 607, 597, 603, 591, 610, 1192,
1000000, 2424, 604, 1198, 604, 590, 611, 1191, 610, 594, 606, 598, 603, 1199, 602, 1200, 602, 592, 609, 595, 605, 600, 601, 593, 608, 1194,
25386, 2419, 610, 1192, 610, 594, 606, 1196, 607, 597, 603, 591, 610, 1192, 609, 1193, 610, 594, 606, 598, 602, 592, 609, 595, 605, 1197,
25385, 2421, 608, 1194, 608, 596, 605, 1197, 605, 599, 601, 593, 608, 1194, 608, 1194, 608, 596, 605, 589, 611, 594, 607, 597, 604, 1198,
1000000, 2426, 603, 1199, 602, 1200, 602, 1200, 602, 592, 608, 1194, 608, 596, 604, 590, 611, 594, 607, 597, 603, 591, 610, 594, 606, 1196, 605, 600, 601, 593, 608, 596, 604, 590, 610, 594, 607, 1195, 606, 598, 603, 591,
15078, 2419, 610, 1192, 610, 1192, 610, 1192, 610, 594, 606, 1196, 605, 600, 601, 593, 608, 597, 604, 590, 610, 595, 606, 598, 602, 1200, 602, 592, 608, 597, 604, 590, 611, 594, 607, 597, 603, 1199, 603, 591, 609, 595,
15075, 2422, 607, 1195, 607, 1195, 607, 1195, 607, 597, 604, 1198, 603, 591, 610, 594, 606, 598, 603, 591, 609, 595, 605, 600, 601, 1191, 611, 594, 607, 597, 603, 591, 610, 594, 606, 598, 602, 1200, 602, 592, 608, 596,
1000000, 2422, 607, 1195, 606, 599, 602, 592, 608, 596, 604, 590, 610, 1192, 610, 594, 606, 599, 602, 592, 608, 596, 604, 590, 610, 1192,
26585, 2426, 602, 1200, 602, 592, 608, 596, 604, 590, 611, 594, 607, 1195, 607, 598, 603, 591, 610, 594, 606, 598, 603, 591, 609, 1193,
26586, 2425, 604, 1198, 603, 591, 610, 594, 606, 598, 602, 592, 609, 1193, 608, 597, 605, 600, 601, 593, 607, 597, 604, 590, 610, 1192,
1000000, 2418, 610, 594, 606, 598, 603, 1199, 603, 1199, 603, 1199, 603, 1199, 603, 1199, 603, 591, 610, 1192, 610, 594, 606, 1196, 606, 1196, 606, 1196, 606, 598, 602, 592, 609, 1193, 609, 1193, 609, 1193, 609, 595, 605, 599,
11557, 2418, 611, 594, 607, 598, 603, 1199, 603, 1199, 603, 1199, 602, 1200, 602, 1200, 601, 593, 608, 1194, 607, 597, 604, 1198, 603, 1199, 603, 1199, 602, 592, 608, 596, 604, 1198, 603, 1199, 603, 1199, 603, 591, 609, 595,
11561, 2424, 604, 590, 610, 594, 607, 1195, 606, 1196, 606, 1196, 606, 1196, 606, 1196, 605, 600, 601, 1191, 611, 594, 607, 1195, 607, 1195, 607, 1195, 607, 597, 603, 591, 610, 1192, 610, 1192, 610, 1192, 610, 594, 606, 598,
1000000, 2424, 604, 590, 611, 594, 607, 1195, 607, 1195, 607, 1195, 607, 1195, 607, 1195, 606, 598, 603, 1199, 602, 592, 609, 1193, 608, 1194, 608, 1194, 608, 596, 604, 590, 611, 1191, 611, 1191, 611, 1191, 611, 594, 607, 598,
11559, 2427, 602, 592, 608, 596, 605, 1197, 604, 1198, 604, 1198, 604, 1198, 604, 1198, 604, 590, 610, 1192, 610, 595, 606, 1196, 606, 1196, 606, 1196, 606, 599, 603, 591, 609, 1193, 609, 1193, 609, 1193, 608, 597, 605, 589,
11567, 2418, 610, 595, 607, 597, 603, 1199, 603, 1199, 602, 1200, 602, 1200, 601, 1201, 601, 593, 608, 1194, 607, 598, 603, 1199, 603, 1199, 603, 1199, 603, 591, 609, 595, 605, 1197, 605, 1197, 605, 1197, 604, 590, 611, 594,
1000000, 2421, 608, 597, 604, 590, 610, 1192, 610, 1192, 609, 1193, 609, 1193, 609, 1193, 608, 596, 605, 1197, 604, 590, 610, 1192, 611, 1191, 610, 1192, 610, 594, 606, 598, 603, 1199, 603, 1199, 602, 1200, 602, 592, 608, 596,
11561, 2424, 604, 590, 610, 594, 606, 1196, 606, 1196, 606, 1196, 606, 1196, 605, 1197, 605, 600, 601, 1201, 601, 593, 607, 1195, 607, 1195, 606, 1196, 606, 598, 602, 592, 608, 1194, 608, 1194, 607, 1195, 607, 597, 603, 591,
11564, 2421, 607, 597, 604, 590, 610, 1192, 610, 1192, 610, 1192, 610, 1192, 610, 1192, 609, 595, 606, 1196, 606, 598, 602, 1200, 601, 1201, 601, 1201, 601, 593, 607, 598, 603, 1199, 603, 1199, 602, 1200, 602, 592, 608, 596,
1000000, 2420, 609, 595, 606, 598, 602, 1200, 602, 1200, 602, 1200, 602, 1200, 602, 1200, 602, 592, 608, 1194, 608, 596, 604, 1198, 603, 1199, 603, 1199, 603, 591, 610, 594, 606, 1196, 606, 1196, 606, 1196, 606, 598, 602, 592,
11565, 2420, 609, 595, 605, 600, 601, 1201, 601, 1201, 601, 1201, 601, 1201, 601, 1201, 601, 593, 607, 1195, 607, 597, 603, 1199, 603, 1199, 603, 1199, 603, 591, 609, 595, 605, 1197, 605, 1197, 605, 1197, 605, 599, 601, 593,
11563, 2422, 607, 597, 603, 591, 609, 1193, 609, 1193, 608, 1194, 608, 1194, 608, 1194, 582, 623, 603, 1199, 603, 591, 610, 1202, 599, 1203, 599, 1203, 599, 595, 581, 623, 577, 1225, 601, 1201, 601, 1201, 601, 593, 582, 623,
1000000, 2425, 602, 1200, 602, 1200, 602, 592, 608, 1194, 608, 1194, 607, 1195, 607, 1195, 607, 597, 603, 1199, 602, 592, 609, 1193, 608, 1194, 608, 1194, 607, 597, 603, 601, 575, 1227, 599, 1203, 599, 1203, 573, 621, 580, 625,
10931, 2426, 578, 1224, 578, 1224, 578, 616, 585, 1217, 585, 1217, 585, 1217, 585, 1217, 585, 620, 580, 1222, 580, 624, 577, 1225, 577, 1225, 577, 1225, 577, 617, 583, 622, 580, 1222, 580, 1222, 579, 1223, 579, 625, 576, 618,
10936, 2421, 583, 1219, 583, 1219, 582, 622, 579, 1223, 578, 1224, 578, 1224, 578, 1224, 578, 616, 584, 1218, 584, 621, 580, 1222, 579, 1223, 579, 1223, 579, 625, 576, 618, 583, 1219, 582, 1220, 582, 1220, 582, 622, 578, 616,
1000000, 2419, 584, 620, 580, 1222, 580, 624, 576, 1226, 576, 1226, 576, 1226, 576, 1226, 576, 618, 582, 1220, 582, 622, 579, 1223, 578, 1224, 579, 1223, 578, 616, 585, 619, 581, 1221, 581, 1221, 581, 1221, 580, 624, 576, 618,
11563, 2422, 582, 622, 579, 1223, 578, 616, 585, 1217, 585, 1217, 584, 1218, 584, 1218, 583, 622, 579, 1223, 579, 625, 575, 1216, 585, 1217, 585, 1217, 585, 619, 581, 623, 577, 1225, 577, 1225, 577, 1225, 576, 618, 583, 621,
11558, 2427, 577, 617, 584, 1218, 583, 621, 579, 1223, 579, 1223, 578, 1224, 578, 1224, 578, 647, 553, 1249, 553, 651, 549, 1253, 548, 1254, 549, 1253, 549, 645, 555, 649, 551, 1251, 551, 1251, 551, 1251, 550, 654, 546, 648,
1000000, 2456, 548, 646, 554, 650, 551, 653, 547, 1255, 547, 1255, 547, 1255, 547, 1255, 547, 647, 554, 1248, 554, 650, 551, 1251, 551, 1251, 551, 1251, 551, 653, 547, 647, 554, 1248, 554, 1248, 554, 1248, 553, 651, 550, 644,
12112, 2449, 555, 649, 551, 654, 547, 647, 554, 1248, 554, 1248, 553, 1249, 553, 1249, 553, 651, 549, 1253, 549, 645, 555, 1247, 555, 1247, 555, 1247, 554, 650, 550, 655, 547, 1244, 557, 1224, 578, 1224, 579, 615, 585, 619,
12139, 2423, 580, 624, 576, 618, 582, 622, 578, 1224, 578, 1224, 578, 1224, 578, 1224, 578, 616, 584, 1218, 584, 620, 580, 1222, 580, 1222, 581, 1221, 580, 624, 577, 617, 584, 1218, 584, 1218, 584, 1218, 584, 620, 581, 623,
1000000, 2422, 582, 1220, 581, 623, 578, 616, 584, 1218, 584, 1218, 584, 1218, 584, 1218, 584, 620, 580, 1222, 580, 624, 576, 1226, 577, 1225, 576, 1226, 576, 618, 583, 622, 579, 1223, 578, 1224, 577, 1225, 577, 617, 585, 619,
11559, 2426, 577, 1225, 577, 617, 583, 621, 579, 1223, 579, 1223, 578, 1224, 578, 1224, 578, 616, 584, 1218, 584, 620, 580, 1222, 580, 1222, 579, 1223, 579, 625, 575, 650, 550, 1252, 550, 1252, 549, 1253, 549, 645, 556, 648,
11531, 2453, 549, 1253, 548, 646, 555, 649, 551, 1251, 551, 1251, 550, 1252, 549, 1253, 549, 645, 556, 1246, 555, 649, 552, 1219, 582, 1220, 582, 1220, 582, 622, 578, 616, 585, 1217, 584, 1218, 584, 1218, 584, 620, 581, 623,
1000000, 2428, 576, 618, 582, 1220, 583, 622, 579, 1223, 578, 1224, 578, 1224, 578, 1224, 578, 616, 585, 1217, 585, 619, 582, 1220, 582, 1220, 582, 1220, 582, 622, 578, 616, 585, 1217, 585, 1217, 584, 1218, 584, 620, 581, 623,
11557, 2427, 577, 617, 584, 1218, 583, 621, 580, 1222, 580, 1222, 580, 1222, 580, 1222, 580, 624, 576, 1246, 555, 649, 552, 1250, 552, 1250, 551, 1251, 551, 653, 548, 646, 554, 1248, 555, 1247, 555, 1247, 555, 649, 551, 653,
11528, 2447, 556, 648, 552, 1250, 552, 653, 548, 1254, 548, 1254, 547, 1245, 557, 1245, 557, 647, 553, 1249, 552, 652, 549, 1253, 548, 1254, 548, 1254, 548, 646, 555, 649, 551, 1251, 551, 1251, 551, 1251, 551, 643, 557, 647,
1000000, 2418, 610, 594, 606, 598, 603, 1199, 603, 1199, 602, 1200, 602, 1200, 602, 1200, 602, 592, 608, 1194, 608, 596, 604, 1198, 604, 1198, 604, 1198, 603, 591, 610, 594, 606, 1196, 606, 1196, 605, 1197, 605, 599, 601, 593,
11563, 2422, 606, 598, 602, 592, 608, 1194, 608, 1194, 608, 1194, 607, 1195, 607, 1195, 607, 597, 603, 1199, 603, 591, 609, 1193, 609, 1193, 609, 1193, 608, 596, 605, 599, 601, 1201, 600, 1191, 611, 1191, 611, 593, 606, 598,
11558, 2427, 601, 593, 607, 597, 603, 1199, 603, 1199, 603, 1199, 602, 1200, 602, 1200, 601, 593, 607, 1195, 607, 597, 603, 1199, 603, 1199, 602, 1200, 602, 592, 608, 596, 604, 1198, 604, 1198, 604, 1198, 603, 591, 609, 595,
1000000, 2424, 605, 1197, 604, 600, 600, 1191, 610, 1192, 610, 1192, 610, 1192, 609, 1193, 609, 595, 605, 1197, 606, 598, 602, 1200, 602, 1200, 601, 1201, 601, 593, 607, 597, 603, 1199, 603, 1199, 603, 1199, 603, 591, 609, 595,
10937, 2420, 609, 1193, 609, 595, 605, 1197, 605, 1197, 605, 1197, 605, 1197, 605, 1197, 605, 600, 601, 1201, 601, 593, 608, 1194, 608, 1194, 608, 1194, 609, 595, 605, 599, 601, 1201, 601, 1201, 601, 1201, 601, 593, 608, 596,
10936, 2420, 608, 1194, 608, 596, 604, 1198, 605, 1197, 605, 1197, 605, 1197, 605, 1197, 605, 600, 601, 1201, 601, 593, 607, 1195, 608, 1194, 608, 1194, 608, 596, 604, 600, 600, 1202, 601, 1201, 601, 1201, 601, 593, 607, 597,
1000000, 2420, 609, 1193, 608, 1194, 608, 596, 579, 625, 600, 1202, 600, 1202, 600, 1202, 600, 594, 607, 1195, 607, 597, 603, 1199, 603, 1199, 602, 1200, 602, 592, 609, 595, 605, 1197, 605, 1197, 605, 1197, 604, 600, 600, 594,
11561, 2423, 605, 1197, 605, 1197, 605, 599, 601, 593, 607, 1195, 607, 1195, 607, 1195, 607, 597, 604, 1198, 603, 591, 610, 1192, 610, 1192, 610, 1192, 610, 594, 607, 597, 603, 1199, 603, 1199, 603, 1199, 603, 591, 610, 594,
11563, 2421, 608, 1194, 608, 1194, 608, 596, 605, 599, 601, 1201, 602, 1200, 602, 1200, 602, 592, 609, 1193, 609, 595, 605, 1197, 606, 1196, 606, 1196, 606, 598, 602, 592, 609, 1193, 609, 1193, 610, 1192, 610, 594, 607, 597,
1000000, 2428, 576, 1226, 600, 1192, 610, 594, 606, 598, 602, 1200, 602, 592, 609, 595, 605, 600, 601, 593, 607, 597, 603, 591, 609, 1193,
25955, 2427, 601, 1190, 610, 1192, 610, 594, 606, 598, 602, 1200, 601, 593, 607, 597, 603, 591, 610, 594, 606, 598, 602, 592, 608, 1194,
25957, 2425, 604, 1198, 603, 1199, 603, 591, 609, 595, 605, 1197, 605, 599, 601, 593, 607, 598, 603, 591, 610, 594, 606, 598, 602, 1200,
25952, 2420, 608, 1194, 608, 1194, 608, 596, 604, 601, 600, 1191, 610, 594, 606, 598, 603, 591, 609, 595, 605, 599, 601, 593, 608, 1194,
1000000, 2421, 607, 597, 603, 1199, 603, 591, 610, 594, 606, 1196, 605, 600, 576, 618, 583, 621, 604, 601, 575, 619, 606, 598, 603, 1199,
26576, 2424, 605, 600, 576, 1226, 601, 593, 608, 596, 604, 1198, 604, 600, 600, 594, 607, 597, 603, 591, 609, 595, 606, 598, 602, 1200,
26579, 2420, 583, 621, 605, 1197, 604, 600, 601, 593, 607, 1195, 607, 597, 604, 590, 610, 594, 607, 597, 603, 591, 610, 594, 606, 1196,
26584, 2426, 603, 591, 609, 1193, 609, 595, 605, 599, 601, 1201, 601, 593, 607, 597, 603, 591, 610, 594, 606, 598, 603, 591, 609, 1193,
1000000, 2418, 610, 594, 606, 598, 602, 592, 609, 595, 605, 1197, 605, 1197, 605, 600, 602, 592, 608, 1194, 608, 596, 605, 1197, 605, 1197, 604, 1198, 604, 600, 601, 593, 607, 1195, 607, 1195, 607, 1195, 607, 597, 603, 591,
13368, 2419, 610, 594, 606, 598, 602, 592, 608, 596, 605, 1197, 604, 1198, 604, 600, 601, 593, 607, 1195, 607, 597, 603, 1199, 603, 1199, 603, 1199, 603, 591, 609, 595, 605, 1197, 605, 1197, 605, 1197, 604, 601, 600, 594,
13362, 2425, 604, 601, 600, 594, 607, 597, 603, 591, 610, 1192, 610, 1192, 610, 594, 606, 598, 603, 1199, 602, 592, 609, 1193, 609, 1193, 608, 1194, 608, 596, 604, 601, 600, 1191, 610, 1192, 610, 1192, 610, 594, 607, 597,
1000000, 2427, 601, 1201, 601, 593, 582, 623, 603, 1199, 578, 1224, 603, 1199, 577, 617, 584, 620, 605, 1197, 605, 600, 601, 1201, 601, 1201, 601, 1201, 601, 593, 582, 622, 603, 1199, 603, 1199, 578, 1224, 603, 591, 610, 594,
12139, 2423, 605, 1197, 605, 600, 601, 593, 608, 1194, 608, 1194, 607, 1195, 607, 597, 604, 600, 601, 1201, 601, 593, 607, 1195, 608, 1194, 608, 1194, 583, 622, 604, 601, 600, 1202, 575, 1227, 601, 1201, 576, 618, 607, 597,
12137, 2424, 604, 1198, 604, 590, 610, 594, 607, 1195, 607, 1195, 607, 1195, 607, 597, 604, 601, 600, 1202, 600, 594, 607, 1195, 606, 1196, 581, 1221, 607, 597, 603, 591, 610, 1202, 600, 1202, 576, 1226, 602, 592, 609, 595,
1000000, 2422, 607, 1195, 607, 597, 603, 591, 585, 619, 606, 1196, 606, 1196, 581, 624, 577, 617, 609, 1193, 584, 621, 605, 1197, 604, 1198, 604, 1198, 604, 590, 610, 595, 581, 1221, 605, 1197, 605, 1197, 605, 599, 601, 593,
12763, 2427, 603, 1199, 603, 591, 609, 595, 605, 599, 577, 1225, 602, 1200, 601, 593, 583, 622, 604, 1198, 604, 590, 610, 1192, 610, 1192, 610, 1192, 610, 594, 606, 598, 603, 1199, 603, 1199, 603, 1199, 603, 591, 610, 594,
12763, 2427, 602, 1200, 601, 593, 608, 596, 604, 600, 600, 1202, 601, 1201, 601, 593, 607, 597, 604, 1198, 604, 590, 610, 1192, 610, 1192, 611, 1191, 610, 594, 607, 598, 578, 1224, 603, 1199, 603, 1199, 603, 591, 610, 594,
1000000, 2422, 607, 597, 603, 591, 610, 1192, 610, 594, 606, 1196, 580, 1222, 605, 600, 601, 593, 583, 1219, 608, 596, 579, 1223, 604, 1198, 604, 1198, 604, 590, 610, 594, 606, 1196, 606, 1196, 606, 1196, 606, 599, 602, 592,
12765, 2425, 603, 591, 610, 594, 606, 1196, 606, 598, 602, 1200, 601, 1201, 601, 593, 582, 623, 604, 1198, 604, 590, 610, 1192, 610, 1192, 609, 1193, 609, 596, 605, 599, 601, 1201, 601, 1201, 601, 1201, 600, 594, 607, 597,
12758, 2421, 607, 597, 603, 591, 609, 1193, 609, 595, 605, 1197, 605, 1197, 605, 599, 601, 593, 607, 1195, 607, 597, 603, 1199, 603, 1199, 603, 1199, 603, 591, 609, 595, 580, 1222, 605, 1197, 605, 1197, 605, 600, 600, 594,
1000000, 2422, 580, 625, 601, 1201, 601, 593, 608, 596, 604, 1198, 604, 1198, 604, 600, 600, 594, 607, 1195, 607, 597, 603, 1199, 603, 1199, 603, 1199, 578, 616, 609, 595, 606, 1196, 606, 1196, 606, 1196, 581, 624, 602, 592,
12766, 2424, 605, 599, 601, 1201, 601, 593, 608, 596, 604, 1198, 604, 1198, 579, 625, 601, 593, 608, 1194, 608, 596, 604, 1198, 580, 1222, 605, 1197, 605, 599, 602, 592, 608, 1194, 609, 1193, 609, 1193, 609, 596, 605, 599,
12759, 2420, 608, 597, 604, 1198, 604, 590, 610, 594, 606, 1196, 606, 1196, 606, 598, 602, 592, 608, 1194, 607, 597, 604, 1198, 603, 1199, 603, 1199, 602, 592, 609, 595, 605, 1197, 605, 1197, 605, 1197, 604, 600, 601, 593,
1000000, 2429, 600, 1202, 575, 1227, 599, 595, 606, 598, 602, 1200, 602, 1200, 602, 592, 609, 595, 605, 1197, 605, 599, 601, 1201, 601, 1201, 601, 1201, 601, 593, 607, 597, 603, 1199, 603, 1199, 603, 1199, 603, 591, 609, 595,
12136, 2425, 603, 1199, 603, 1199, 603, 591, 609, 595, 605, 1197, 605, 1197, 605, 599, 601, 593, 608, 1194, 608, 596, 604, 1198, 604, 1198, 603, 1199, 603, 591, 609, 595, 606, 1196, 606, 1196, 605, 1197, 605, 599, 601, 593,
12137, 2424, 604, 1198, 603, 1199, 603, 591, 609, 595, 606, 1196, 605, 1197, 605, 599, 601, 593, 607, 1195, 607, 597, 603, 1199, 603, 1199, 603, 1199, 602, 592, 609, 595, 605, 1197, 605, 1197, 604, 1198, 604, 600, 600, 594,
1000000, 2429, 574, 1228, 573, 1229, 573, 1229, 573, 1229, 573, 621, 581, 624, 577, 617, 583, 622, 580, 1222, 579, 625, 576, 1226, 577, 1225, 576, 1226, 577, 617, 584, 620, 581, 1221, 580, 1222, 579, 1223, 579, 625, 577, 617,
12142, 2419, 584, 1218, 584, 1218, 583, 1219, 583, 1219, 582, 623, 578, 616, 585, 619, 581, 623, 578, 1224, 577, 617, 584, 1218, 584, 1218, 584, 1218, 584, 620, 580, 624, 576, 1226, 575, 1227, 500, 1353, 523, 620, 582, 622, /* failed, noise pollution 500, 1353 timings */
12134, 2427, 576, 1226, 576, 1226, 576, 1226, 576, 1226, 576, 618, 583, 622, 579, 625, 576, 618, 583, 1219, 583, 673, 527, 1223, 579, 1223, 579, 1223, 579, 625, 576, 618, 582, 1220, 582, 1220, 582, 1220, 582, 622, 578, 616,
12140, 2421, 582, 1220, 581, 1221, 581, 1221, 580, 1222, 580, 624, 576, 618, 582, 622, 578, 616, 585, 1217, 584, 620, 581, 1221, 580, 1222, 580, 1222, 580, 624, 576, 618, 582, 1220, 582, 1220, 582, 1220, 582, 623, 578, 616,
1000000, 2419, 584, 672, 528, 625, 577, 617, 583, 1219, 582, 1220, 581, 1221, 582, 622, 578, 616, 585, 1217, 583, 621, 580, 1222, 580, 1222, 580, 1222, 580, 624, 576, 618, 583, 1219, 583, 1219, 582, 1220, 583, 621, 580, 624,
12732, 2427, 577, 617, 584, 672, 528, 676, 524, 1226, 576, 1226, 576, 1226, 576, 618, 583, 621, 579, 1223, 579, 625, 575, 1227, 575, 1227, 575, 1227, 575, 671, 529, 675, 526, 1224, 578, 1224, 578, 1224, 578, 616, 585, 619,
12738, 2421, 583, 621, 580, 624, 576, 618, 582, 1220, 582, 1220, 583, 1219, 583, 621, 579, 625, 576, 1226, 576, 670, 530, 1220, 582, 1220, 582, 1220, 581, 624, 577, 617, 584, 1218, 583, 1219, 583, 1219, 583, 622, 580, 624,
1000000, 2430, 599, 1192, 609, 595, 605, 1197, 580, 624, 601, 1201, 601, 593, 607, 597, 603, 591, 610, 594, 606, 598, 602, 592, 608, 1194,
25958, 2423, 605, 1197, 604, 600, 575, 1227, 600, 594, 606, 1196, 606, 598, 602, 592, 608, 596, 604, 601, 600, 594, 607, 597, 603, 1199,
25949, 2422, 607, 1195, 606, 598, 603, 1199, 602, 592, 608, 1194, 608, 596, 604, 600, 601, 593, 607, 597, 604, 600, 600, 594, 607, 1195,
25958, 2423, 606, 1196, 606, 598, 602, 1200, 602, 592, 608, 1194, 608, 596, 605, 600, 601, 593, 607, 597, 603, 591, 610, 594, 606, 1196,
25957, 2425, 604, 1198, 603, 591, 610, 1192, 610, 594, 606, 1196, 606, 598, 602, 592, 609, 595, 605, 599, 602, 592, 608, 596, 604, 1198,
25956, 2426, 604, 1198, 603, 591, 610, 1192, 610, 594, 606, 1196, 606, 598, 602, 592, 608, 597, 605, 599, 601, 593, 607, 597, 603, 1199,
25952, 2420, 609, 1193, 608, 596, 604, 1198, 604, 590, 610, 1192, 610, 594, 606, 598, 602, 592, 608, 596, 605, 599, 601, 593, 607, 1195,
1000000, 2422, 583, 1219, 608, 596, 605, 1197, 580, 624, 601, 1201, 601, 593, 608, 596, 604, 600, 601, 593, 607, 597, 604, 590, 610, 1202,
25955, 2427, 603, 1199, 603, 591, 609, 1193, 610, 594, 606, 1196, 607, 597, 603, 591, 609, 595, 606, 598, 602, 592, 609, 595, 605, 1197,
25957, 2425, 604, 1198, 604, 590, 610, 1192, 610, 594, 581, 1221, 606, 598, 602, 592, 608, 596, 605, 599, 601, 593, 607, 597, 604, 1198,
25954, 2418, 610, 1192, 610, 594, 606, 1196, 605, 599, 601, 1201, 601, 593, 608, 596, 604, 590, 610, 594, 606, 598, 603, 591, 609, 1193,
25958, 2424, 604, 1198, 604, 590, 610, 1192, 610, 594, 606, 1196, 606, 598, 602, 592, 609, 595, 605, 600, 601, 593, 607, 597, 603, 1199,
25953, 2419, 610, 1192, 609, 595, 605, 1197, 605, 600, 601, 1201, 600, 594, 607, 597, 603, 591, 609, 595, 605, 599, 601, 593, 608, 1194,
25954, 2418, 611, 1191, 610, 594, 606, 1196, 606, 598, 602, 1200, 602, 592, 608, 596, 604, 601, 600, 594, 607, 597, 603, 591, 609, 1193,
25958, 2424, 605, 1197, 605, 599, 601, 1201, 600, 594, 607, 1195, 607, 597, 603, 591, 609, 596, 605, 599, 602, 592, 608, 596, 605, 1197,
25954, 2428, 601, 1190, 610, 594, 607, 1195, 606, 598, 602, 1200, 602, 592, 608, 596, 604, 590, 610, 594, 606, 598, 603, 591, 609, 1193,
25960, 2422, 607, 1195, 606, 598, 602, 1200, 602, 592, 609, 1193, 609, 595, 605, 599, 601, 593, 607, 597, 604, 590, 610, 594, 606, 1196,
25957, 2424, 605, 1197, 604, 600, 600, 1202, 601, 593, 607, 1195, 607, 597, 603, 591, 610, 594, 606, 598, 603, 591, 609, 595, 606, 1196,
1000000, 2421, 608, 1194, 608, 596, 604, 1198, 604, 601, 575, 1227, 601, 593, 607, 598, 604, 600, 600, 594, 607, 598, 604, 601, 600, 1202,
25953, 2419, 609, 1193, 609, 596, 605, 1197, 605, 600, 601, 1201, 601, 593, 607, 597, 603, 591, 610, 594, 606, 599, 602, 592, 608, 1194,
25958, 2424, 605, 1197, 605, 600, 601, 1201, 600, 594, 607, 1195, 606, 598, 602, 592, 609, 595, 605, 600, 601, 593, 608, 596, 604, 1198,
1000000, 2423, 606, 1196, 606, 598, 602, 1200, 602, 592, 608, 1194, 608, 597, 604, 590, 610, 594, 607, 597, 603, 591, 610, 594, 606, 1196,
25958, 2424, 605, 1197, 604, 600, 601, 1201, 601, 593, 607, 1195, 607, 597, 604, 590, 610, 595, 607, 597, 603, 591, 610, 594, 607, 1195,
25961, 2421, 608, 1194, 608, 596, 605, 1197, 605, 600, 601, 1201, 601, 593, 607, 597, 603, 591, 610, 594, 606, 598, 602, 592, 609, 1193,
1000000, 2429, 601, 1201, 601, 593, 607, 1195, 608, 596, 604, 1198, 604, 600, 601, 593, 607, 597, 604, 590, 611, 593, 607, 597, 604, 1198,
25959, 2422, 607, 1195, 607, 597, 603, 1199, 603, 591, 609, 1193, 610, 594, 606, 598, 602, 592, 609, 595, 605, 599, 602, 592, 608, 1194,
25959, 2421, 608, 1194, 607, 597, 603, 1199, 603, 591, 610, 1192, 610, 594, 606, 598, 603, 591, 609, 595, 605, 599, 602, 592, 608, 1194,
25959, 2422, 608, 1194, 608, 596, 604, 1198, 604, 590, 611, 1201, 601, 593, 607, 597, 604, 590, 610, 594, 607, 597, 603, 591, 609, 1193,
25962, 2418, 610, 1192, 610, 594, 606, 1196, 607, 597, 603, 1199, 603, 591, 609, 595, 605, 599, 602, 592, 608, 596, 605, 599, 601, 1201,
1000000, 2357, 610, 1183, 592, 1191, 614, 1179, 595, 1188, 618, 584, 613, 1180, 593, 588, 613, 1190, 612, 590, 605, 587, 613, 589, 605, 587,
25398, 2355, 623, 1180, 591, 1181, 627, 1186, 589, 1183, 618, 584, 616, 1187, 587, 584, 614, 1189, 615, 587, 605, 587, 615, 587, 609, 593,
1000000, 2383, 609, 1214, 600, 612, 580, 1213, 608, 614, 583, 1210, 605, 607, 586, 616, 611, 1192, 599, 613, 608, 614, 579, 613, 615, 607,
26670, 2387, 601, 1212, 600, 612, 589, 1214, 603, 609, 586, 1217, 595, 607, 594, 618, 606, 1187, 602, 610, 609, 613, 588, 614, 610, 612,
1000000, 2445, 582, 1221, 603, 548, 602, 1191, 609, 572, 602, 1191, 609, 542, 607, 544, 631, 1172, 603, 568, 606, 545, 605, 566, 608, 543,
26263, 2414, 611, 1192, 607, 544, 606, 1197, 602, 569, 606, 1197, 602, 539, 611, 540, 635, 1168, 606, 565, 610, 541, 608, 563, 587, 564,
};
const IrdaMessage test_decoder_sirc_expected1[] = {
{IrdaProtocolSIRC, 0x10, 0x15, false},
{IrdaProtocolSIRC, 0x10, 0x15, false},
{IrdaProtocolSIRC, 0x10, 0x15, false},
{IrdaProtocolSIRC, 0x10, 0x15, false},
{IrdaProtocolSIRC, 0x10, 0x15, false},
{IrdaProtocolSIRC, 0x10, 0x15, false},
{IrdaProtocolSIRC, 0x10, 0x60, false},
{IrdaProtocolSIRC, 0x10, 0x60, false},
{IrdaProtocolSIRC, 0x10, 0x60, false},
{IrdaProtocolSIRC, 0x10, 0x65, false},
{IrdaProtocolSIRC, 0x10, 0x65, false},
{IrdaProtocolSIRC, 0x10, 0x65, false},
{IrdaProtocolSIRC20, 0x410, 0x17, false},
{IrdaProtocolSIRC20, 0x410, 0x17, false},
{IrdaProtocolSIRC20, 0x410, 0x17, false},
{IrdaProtocolSIRC, 0x10, 0x21, false},
{IrdaProtocolSIRC, 0x10, 0x21, false},
{IrdaProtocolSIRC, 0x10, 0x21, false},
{IrdaProtocolSIRC20, 0x73A, 0x7C, false},
{IrdaProtocolSIRC20, 0x73A, 0x7C, false},
{IrdaProtocolSIRC20, 0x73A, 0x7C, false},
{IrdaProtocolSIRC20, 0x73A, 0x7C, false},
{IrdaProtocolSIRC20, 0x73A, 0x7C, false},
{IrdaProtocolSIRC20, 0x73A, 0x7C, false},
{IrdaProtocolSIRC20, 0x73A, 0x7C, false},
{IrdaProtocolSIRC20, 0x73A, 0x7C, false},
{IrdaProtocolSIRC20, 0x73A, 0x7C, false},
{IrdaProtocolSIRC20, 0x73A, 0x7C, false},
{IrdaProtocolSIRC20, 0x73A, 0x7C, false},
{IrdaProtocolSIRC20, 0x73A, 0x7C, false},
{IrdaProtocolSIRC20, 0x73A, 0x7B, false},
{IrdaProtocolSIRC20, 0x73A, 0x7B, false},
{IrdaProtocolSIRC20, 0x73A, 0x7B, false},
{IrdaProtocolSIRC20, 0x73A, 0x7A, false},
{IrdaProtocolSIRC20, 0x73A, 0x7A, false},
{IrdaProtocolSIRC20, 0x73A, 0x7A, false},
{IrdaProtocolSIRC20, 0x73A, 0x78, false},
{IrdaProtocolSIRC20, 0x73A, 0x78, false},
{IrdaProtocolSIRC20, 0x73A, 0x78, false},
{IrdaProtocolSIRC20, 0x73A, 0x79, false},
{IrdaProtocolSIRC20, 0x73A, 0x79, false},
{IrdaProtocolSIRC20, 0x73A, 0x79, false},
{IrdaProtocolSIRC20, 0x73A, 0x7A, false},
{IrdaProtocolSIRC20, 0x73A, 0x7A, false},
{IrdaProtocolSIRC20, 0x73A, 0x7A, false},
{IrdaProtocolSIRC20, 0x73A, 0x7C, false},
{IrdaProtocolSIRC20, 0x73A, 0x7C, false},
{IrdaProtocolSIRC20, 0x73A, 0x7C, false},
{IrdaProtocolSIRC20, 0x73A, 0x7D, false},
{IrdaProtocolSIRC20, 0x73A, 0x7D, false},
{IrdaProtocolSIRC20, 0x73A, 0x7D, false},
{IrdaProtocolSIRC20, 0x73A, 0x73, false},
{IrdaProtocolSIRC20, 0x73A, 0x73, false},
{IrdaProtocolSIRC20, 0x73A, 0x73, false},
{IrdaProtocolSIRC, 0x10, 0x13, false},
{IrdaProtocolSIRC, 0x10, 0x13, false},
{IrdaProtocolSIRC, 0x10, 0x13, false},
{IrdaProtocolSIRC, 0x10, 0x13, false},
{IrdaProtocolSIRC, 0x10, 0x12, false},
{IrdaProtocolSIRC, 0x10, 0x12, false},
{IrdaProtocolSIRC, 0x10, 0x12, false},
{IrdaProtocolSIRC, 0x10, 0x12, false},
{IrdaProtocolSIRC20, 0x73A, 0x30, false},
{IrdaProtocolSIRC20, 0x73A, 0x30, false},
{IrdaProtocolSIRC20, 0x73A, 0x30, false},
{IrdaProtocolSIRC20, 0x73A, 0x39, false},
{IrdaProtocolSIRC20, 0x73A, 0x39, false},
{IrdaProtocolSIRC20, 0x73A, 0x39, false},
{IrdaProtocolSIRC20, 0x73A, 0x31, false},
{IrdaProtocolSIRC20, 0x73A, 0x31, false},
{IrdaProtocolSIRC20, 0x73A, 0x31, false},
{IrdaProtocolSIRC20, 0x73A, 0x34, false},
{IrdaProtocolSIRC20, 0x73A, 0x34, false},
{IrdaProtocolSIRC20, 0x73A, 0x34, false},
{IrdaProtocolSIRC20, 0x73A, 0x32, false},
{IrdaProtocolSIRC20, 0x73A, 0x32, false},
{IrdaProtocolSIRC20, 0x73A, 0x32, false},
{IrdaProtocolSIRC20, 0x73A, 0x33, false},
{IrdaProtocolSIRC20, 0x73A, 0x33, false},
{IrdaProtocolSIRC20, 0x73A, 0x33, false},
{IrdaProtocolSIRC20, 0x73A, 0x0F, false},
{IrdaProtocolSIRC20, 0x73A, 0x0F, false},
{IrdaProtocolSIRC20, 0x73A, 0x0F, false},
{IrdaProtocolSIRC20, 0x73A, 0x38, false},
{IrdaProtocolSIRC20, 0x73A, 0x38, false},
{IrdaProtocolSIRC20, 0x73A, 0x38, false},
{IrdaProtocolSIRC, 0x10, 0x15, false},
{IrdaProtocolSIRC, 0x10, 0x15, false},
{IrdaProtocolSIRC, 0x10, 0x15, false},
{IrdaProtocolSIRC, 0x10, 0x15, false},
{IrdaProtocolSIRC, 0x10, 0x15, false},
{IrdaProtocolSIRC, 0x10, 0x15, false},
{IrdaProtocolSIRC, 0x10, 0x15, false},
{IrdaProtocolSIRC, 0x10, 0x15, false},
{IrdaProtocolSIRC, 0x10, 0x15, false},
{IrdaProtocolSIRC, 0x10, 0x15, false},
{IrdaProtocolSIRC, 0x10, 0x15, false},
{IrdaProtocolSIRC, 0x10, 0x15, false},
{IrdaProtocolSIRC, 0x10, 0x15, false},
{IrdaProtocolSIRC, 0x10, 0x15, false},
{IrdaProtocolSIRC, 0x10, 0x15, false},
{IrdaProtocolSIRC, 0x10, 0x15, false},
{IrdaProtocolSIRC, 0x10, 0x15, false},
{IrdaProtocolSIRC, 0x10, 0x15, false},
{IrdaProtocolSIRC, 0x10, 0x15, false},
{IrdaProtocolSIRC, 0x10, 0x15, false},
{IrdaProtocolSIRC, 0x10, 0x15, false},
{IrdaProtocolSIRC, 0x10, 0x15, false},
{IrdaProtocolSIRC, 0x10, 0x15, false},
{IrdaProtocolSIRC, 0x10, 0x15, false},
{IrdaProtocolSIRC, 0x10, 0x15, false},
{IrdaProtocolSIRC, 0x10, 0x15, false},
{IrdaProtocolSIRC, 0x10, 0x15, false},
{IrdaProtocolSIRC, 0x10, 0x15, false},
{IrdaProtocolSIRC, 0x10, 0x15, false},
{IrdaProtocolSIRC, 0x01, 0x2F, false},
{IrdaProtocolSIRC, 0x01, 0x2F, false},
{IrdaProtocolSIRC, 0x01, 0x15, false},
{IrdaProtocolSIRC, 0x01, 0x15, false},
{IrdaProtocolSIRC, 0x01, 0x15, false},
{IrdaProtocolSIRC, 0x01, 0x15, false},
};
// command (0x55) address (0x0A) SIRC
// 1 0 1 0 1 0 1 0 1 0 1 0
// 2400, 600, 1200, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 600,
//
// command (0x7F) address (0x1F) SIRC
// 1 1 1 1 1 1 1 1 1 1 1 1
// 2400, 600, 1200, 600, 1200, 600, 1200, 600, 1200, 600, 1200, 600, 1200, 600, 1200, 600, 1200, 600, 1200, 600, 1200, 600, 1200, 600, 1200,
//
// command (0x00) address (0x00) SIRC
// 0 0 0 0 0 0 0 0 0 0 0 0
// 2400, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600,
//
// command (0x53) address (0x0D) SIRC
// 1 1 0 0 1 0 1 1 0 1 1 0
// 2400, 600, 1200, 600, 1200, 600, 600, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 1200, 600, 600, 600, 1200, 600, 1200, 600, 600, 600,
const uint32_t test_decoder_sirc_input2[] = {
1000000, 2400, 600, 1200, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 600,
1000000, 2400, 600, 1200, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 600,
1000000, 2400, 600, 1200, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 600,
1000000, 2400, 600, 1200, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 600, 600, 600, /* failed - should be skipped */
1000000, 2400, 600, 1200, 600, 1200, 600, 1200, 600, 1200, 600, 1200, 600, 1200, 600, 1200, 600, 1200, 600, 1200, 600, 1200, 600, 1200, 600, 1200,
1000000, 2400, 600, 1200, 600, 600, /* failed - should be skipped */
1000000, 2400, 600, 1200, 600, 1200, 600, 1200, 600, 1200, 600, 1200, 600, 1200, 600, 1200, 600, 1200, 600, 1200, 600, 1200, 600, 1200, 600, 1200,
1000000, 2400, 600, 1200, 600, /* failed - should be skipped */
2400, 600, 1200, 600, 1200, 600, 1200, 600, 1200, 600, 1200, 600, 1200, 600, 1200, 600, 1200, 600, 1200, 600, 1200, 600, 1200, 600, 1200,
1000000, 2400, 600, 1200, 600, 600, /* failed - should be skipped */
1000000, 2400, 600, 1200, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 600,
1000000, 2400, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600,
1000000, 2400, 600, 1200, 600, 1200, 600, 600, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 1200, 600, 600, 600, 1200, 600, 1200, 600, 600, 600,
};
const IrdaMessage test_decoder_sirc_expected2[] = {
{IrdaProtocolSIRC, 0xA, 0x55, false},
{IrdaProtocolSIRC, 0xA, 0x55, false},
{IrdaProtocolSIRC, 0xA, 0x55, false},
/* failed - 13 data bits */
{IrdaProtocolSIRC, 0x1F, 0x7F, false},
/* failed - 2 data bits */
{IrdaProtocolSIRC, 0x1F, 0x7F, false},
/* failed - sudden end */
{IrdaProtocolSIRC, 0x1F, 0x7F, false},
/* failed */
{IrdaProtocolSIRC, 0x0A, 0x55, false},
{IrdaProtocolSIRC, 0x00, 0x00, false},
{IrdaProtocolSIRC, 0x0D, 0x53, false},
};
// command (0x13) address (0xFD) SIRC15
// 1 1 0 0 1 0 0 1 0 1 1 1 1 1 1
// 2400, 600, 1200, 600, 1200, 600, 600, 600, 600, 600, 1200, 600, 600, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 1200, 600, 1200, 600, 1200, 600, 1200, 600, 1200,
//
// command (0x53) address (0x7D) SIRC15
// 1 1 0 0 1 0 1 1 0 1 1 1 1 1 0
// 2400, 600, 1200, 600, 1200, 600, 600, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 1200, 600, 600, 600, 1200, 600, 1200, 600, 1200, 600, 1200, 600, 1200, 600, 600,
//
// command (0x53) address (0x0D) SIRC15
// 1 1 0 0 1 0 1 1 0 1 1 0 0 0 0
// 2400, 600, 1200, 600, 1200, 600, 600, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 1200, 600, 600, 600, 1200, 600, 1200, 600, 600, 600, 600, 600, 600, 600, 600,
const uint32_t test_decoder_sirc_input3[] = {
1000000, 2400, 600, 1200, 600, 1200, 600, 600, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 1200, 600, 600, 600, 1200, 600, 1200, 600, 1200, 600, 1200, 600, 1200, 600, 600,
10000, 2400, 600, 1200, 600, 1200, 600, 600, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 1200, 600, 600, 600, 1200, 600, 1200, 600, 1200, 600, 1200, 600, 1200, 600, 600,
10000, 2400, 600, 1200, 600, 1200, 600, 600, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 1200, 600, 600, 600, 1200, 600, 1200, 600, 1200, 600, 1200, 600, 1200, 600, 600,
1000000, 2400, 600, 1200, 600, 1200, 600, 600, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 1200, 600, 600, 600, 1200, 600, 1200, 600, 600, 600, 600, 600, 600, 600, 600,
10000, 2400, 600, 1200, 600, 1200, 600, 600, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 1200, 600, 600, 600, 1200, 600, 1200, 600, 600, 600, 600, 600, 600, 600, 600,
10000, 2400, 600, 1200, 600, 1200, 600, 600, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 1200, 600, 600, 600, 1200, 600, 1200, 600, 600, 600, 600, 600, 600, 600, 600,
1000000, 2400, 600, 1200, 600, 1200, 600, 600, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 1200, 600, 600, 600, 1200, 600, 1200, 600, 600, 600, 600, 600, 600, 600, 600,
1000000, 2400, 600, 1200, 600, 1200, 600, 600, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 1200, 600, 600, 600, 1200, 600, 1200, 600, 600, 600, 600, 600, 600, 600, 600,
1000000, 2400, 600, 1200, 600, 1200, 600, 600, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 1200, 600, 600, 600, 1200, 600, 1200, 600, 1200, 600, 1200, 600, 1200, 600, 600,
1000000, 2400, 600, 1200, 600, 1200, 600, 600, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 1200, 600, 600, 600, 1200, 600, 1200, 600, 1200, 600, 1200, 600, 1200, 600, 600,
10000, 2400, 600, 1200, 600, 1200, 600, 600, 600, 600, 600, 1200, 600, 600, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 1200, 600, 1200, 600, 1200, 600, 1200, 600, 1200,
};
const IrdaMessage test_decoder_sirc_expected3[] = {
{IrdaProtocolSIRC15, 0x7D, 0x53, false},
{IrdaProtocolSIRC15, 0x7D, 0x53, false},
{IrdaProtocolSIRC15, 0x7D, 0x53, false},
{IrdaProtocolSIRC15, 0x0D, 0x53, false},
{IrdaProtocolSIRC15, 0x0D, 0x53, false},
{IrdaProtocolSIRC15, 0x0D, 0x53, false},
{IrdaProtocolSIRC15, 0x0D, 0x53, false},
{IrdaProtocolSIRC15, 0x0D, 0x53, false},
{IrdaProtocolSIRC15, 0x7D, 0x53, false},
{IrdaProtocolSIRC15, 0x7D, 0x53, false},
{IrdaProtocolSIRC15, 0xFD, 0x13, false},
};
const uint32_t test_decoder_sirc_input4[] = {
1000000, 2400, 600, 1200, 600, 1200, 600, 600, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 1200, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 1200, 600, 600, 600, 1200, 600, 1200, 600, 1200, 600, 1200, 600, 1200, 600, 600,
10000, 2400, 600, 1200, 600, 1200, 600, 600, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 1200, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 1200, 600, 600, 600, 1200, 600, 1200, 600, 1200, 600, 1200, 600, 1200, 600, 600,
10000, 2400, 600, 1200, 600, 1200, 600, 600, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 1200, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 1200, 600, 600, 600, 1200, 600, 1200, 600, 1200, 600, 1200, 600, 1200, 600, 600,
1000000, 2400, 600, 1200, 600, 1200, 600, 600, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 1200, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 1200, 600, 600, 600, 1200, 600, 1200, 600, 1200, 600, 1200, 600, 1200, 600, 600,
1000000, 2400, 600, 1200, 600, 1200, 600, 600, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 1200, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 1200, 600, 600, 600, 1200, 600, 1200, 600, 1200, 600, 1200, 600, 1200, 600, 600,
1000000, 2400, 600, 1200, 600, 1200, 600, 600, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 1200, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 1200, 600, 600, 600, 1200, 600, 1200, 600, 1200, 600, 1200, 600, 1200, 600, 600,
};
const IrdaMessage test_decoder_sirc_expected4[] = {
{IrdaProtocolSIRC20, 0xFB5, 0x53, false}, // {IrdaProtocolSIRC20, 0x15, 0x3ED3, false},
{IrdaProtocolSIRC20, 0xFB5, 0x53, false},
{IrdaProtocolSIRC20, 0xFB5, 0x53, false},
{IrdaProtocolSIRC20, 0xFB5, 0x53, false},
{IrdaProtocolSIRC20, 0xFB5, 0x53, false},
{IrdaProtocolSIRC20, 0xFB5, 0x53, false},
};
const uint32_t test_decoder_sirc_input5[] = {
1000000, 2400, 600, 1200, 600, 1200, 600, 600, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 1200, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 1200, 600, 600, 600, 1200, 600, 1200, 600, 1200, 600, 1200, 600, 1200, 600, 600,
1000000, 2400, 600, 1200, 600, 1200, 600, 600, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 1200, 600, 600, 600, 1200, 600, 1200, 600, 1200, 600, 1200, 600, 1200, 600, 600,
1000000, 2400, 600, 1200, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 600,
1000000, 2400, 600, 1200, 600, 1200, 600, 600, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 1200, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 1200, 600, 600, 600, 1200, 600, 1200, 600, 1200, 600, 1200, 600, 1200, 600, 600,
1000000, 2400, 600, 1200, 600, 1200, 600, 600, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 1200, 600, 600, 600, 1200, 600, 1200, 600, 1200, 600, 1200, 600, 1200, 600, 600,
1000000, 2400, 600, 1200, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 600,
10000, 2400, 600, 1200, 600, 1200, 600, 600, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 1200, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 1200, 600, 600, 600, 1200, 600, 1200, 600, 1200, 600, 1200, 600, 1200, 600, 600,
10000, 2400, 600, 1200, 600, 1200, 600, 600, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 1200, 600, 600, 600, 1200, 600, 1200, 600, 1200, 600, 1200, 600, 1200, 600, 600,
10000, 2400, 600, 1200, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 600,
10000, 2400, 600, 1200, 600, 1200, 600, 600, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 1200, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 1200, 600, 600, 600, 1200, 600, 1200, 600, 1200, 600, 1200, 600, 1200, 600, 600,
10000, 2400, 600, 1200, 600, 1200, 600, 600, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 1200, 600, 600, 600, 1200, 600, 1200, 600, 1200, 600, 1200, 600, 1200, 600, 600,
10000, 2400, 600, 1200, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 600,
10000, 2400, 600, 1200, 600, 1200, 600, 600, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 1200, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 1200, 600, 600, 600, 1200, 600, 1200, 600, 1200, 600, 1200, 600, 1200, 600, 600,
10000, 2400, 600, 1200, 600, 1200, 600, 600, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 1200, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 1200, 600, 600, 600, 1200, 600, 1200, 600, 1200, 600, 1200, 600, 1200, 600, 600,
10000, 2400, 600, 1200, 600, 1200, 600, 600, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 1200, 600, 600, 600, 1200, 600, 1200, 600, 1200, 600, 1200, 600, 1200, 600, 600,
10000, 2400, 600, 1200, 600, 1200, 600, 600, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 1200, 600, 600, 600, 1200, 600, 1200, 600, 1200, 600, 1200, 600, 1200, 600, 600,
10000, 2400, 600, 1200, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 600,
10000, 2400, 600, 1200, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 600,
10000, 2400, 600, 1200, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 600,
10000, 2400, 600, 1200, 600, 1200, 600, 600, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 1200, 600, 600, 600, 1200, 600, 1200, 600, 1200, 600, 1200, 600, 1200, 600, 600,
10000, 2400, 600, 1200, 600, 1200, 600, 600, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 1200, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 1200, 600, 600, 600, 1200, 600, 1200, 600, 1200, 600, 1200, 600, 1200, 600, 600,
10000, 2400, 600, 1200, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 600,
10000, 2400, 600, 1200, 600, 1200, 600, 600, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 1200, 600, 600, 600, 1200, 600, 1200, 600, 1200, 600, 1200, 600, 1200, 600, 600,
10000, 2400, 600, 1200, 600, 1200, 600, 600, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 1200, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 1200, 600, 600, 600, 1200, 600, 1200, 600, 1200, 600, 1200, 600, 1200, 600, 600,
};
const IrdaMessage test_decoder_sirc_expected5[] = {
{IrdaProtocolSIRC20, 0xFB5, 0x53, false},
{IrdaProtocolSIRC15, 0x7D, 0x53, false},
{IrdaProtocolSIRC, 0xA, 0x55, false},
{IrdaProtocolSIRC20, 0xFB5, 0x53, false},
{IrdaProtocolSIRC15, 0x7D, 0x53, false},
{IrdaProtocolSIRC, 0xA, 0x55, false},
{IrdaProtocolSIRC20, 0xFB5, 0x53, false},
{IrdaProtocolSIRC15, 0x7D, 0x53, false},
{IrdaProtocolSIRC, 0xA, 0x55, false},
{IrdaProtocolSIRC20, 0xFB5, 0x53, false},
{IrdaProtocolSIRC15, 0x7D, 0x53, false},
{IrdaProtocolSIRC, 0xA, 0x55, false},
{IrdaProtocolSIRC20, 0xFB5, 0x53, false},
{IrdaProtocolSIRC20, 0xFB5, 0x53, false},
{IrdaProtocolSIRC15, 0x7D, 0x53, false},
{IrdaProtocolSIRC15, 0x7D, 0x53, false},
{IrdaProtocolSIRC, 0xA, 0x55, false},
{IrdaProtocolSIRC, 0xA, 0x55, false},
{IrdaProtocolSIRC, 0xA, 0x55, false},
{IrdaProtocolSIRC15, 0x7D, 0x53, false},
{IrdaProtocolSIRC20, 0xFB5, 0x53, false},
{IrdaProtocolSIRC, 0xA, 0x55, false},
{IrdaProtocolSIRC15, 0x7D, 0x53, false},
{IrdaProtocolSIRC20, 0xFB5, 0x53, false},
};
const IrdaMessage test_encoder_sirc_input1[] = {
{IrdaProtocolSIRC, 0xA, 0x55, false},
};
const uint32_t test_encoder_sirc_expected1[] = {
10000, 2400, 600, 1200, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 600, 600,
};
const IrdaMessage test_encoder_sirc_input2[] = {
{IrdaProtocolSIRC15, 0x7D, 0x53, false},
{IrdaProtocolSIRC15, 0x7D, 0x53, true},
{IrdaProtocolSIRC15, 0x7D, 0x53, true},
};
const uint32_t test_encoder_sirc_expected2[] = {
10000, 2400, 600, 1200, 600, 1200, 600, 600, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 1200, 600, 600, 600, 1200, 600, 1200, 600, 1200, 600, 1200, 600, 1200, 600, 600, 600, /* 2 low levels in row */
18000, 2400, 600, 1200, 600, 1200, 600, 600, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 1200, 600, 600, 600, 1200, 600, 1200, 600, 1200, 600, 1200, 600, 1200, 600, 600, 600, /* 2 low levels in row */
18000, 2400, 600, 1200, 600, 1200, 600, 600, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 1200, 600, 600, 600, 1200, 600, 1200, 600, 1200, 600, 1200, 600, 1200, 600, 600, 600,
};
const IrdaMessage test_sirc[] = {
{IrdaProtocolSIRC20, 0x1FFF, 0x7F, false},
{IrdaProtocolSIRC20, 0x1FFF, 0x7F, true},
{IrdaProtocolSIRC20, 0x1FFF, 0x7F, true},
{IrdaProtocolSIRC, 0x00, 0x00, false},
{IrdaProtocolSIRC, 0x00, 0x00, true},
{IrdaProtocolSIRC, 0x00, 0x00, true},
{IrdaProtocolSIRC, 0x1A, 0x22, false},
{IrdaProtocolSIRC, 0x1A, 0x22, true},
{IrdaProtocolSIRC, 0x1A, 0x22, true},
{IrdaProtocolSIRC, 0x17, 0x0A, false},
{IrdaProtocolSIRC15, 0x7D, 0x53, false},
{IrdaProtocolSIRC15, 0x7D, 0x53, true},
{IrdaProtocolSIRC15, 0x7D, 0x53, true},
{IrdaProtocolSIRC15, 0x71, 0x0, false},
{IrdaProtocolSIRC15, 0x15, 0x01, false},
{IrdaProtocolSIRC15, 0x01, 0x15, false},
{IrdaProtocolSIRC20, 0xAA, 0x55, false},
{IrdaProtocolSIRC20, 0x331, 0x71, false},
{IrdaProtocolSIRC, 0x00, 0x00, false},
{IrdaProtocolSIRC, 0x1F, 0x7F, false},
{IrdaProtocolSIRC15, 0x00, 0x00, false},
{IrdaProtocolSIRC15, 0xFF, 0x7F, false},
{IrdaProtocolSIRC20, 0x00, 0x00, false},
{IrdaProtocolSIRC20, 0x1FFF, 0x7F, false},
};

View File

@ -4,7 +4,7 @@ include $(PROJECT_ROOT)/assets/assets.mk
$(ASSETS): $(ASSETS_SOURCES) $(ASSETS_COMPILLER)
@echo "\tASSETS\t" $@
@$(ASSETS_COMPILLER) icons -s $(ASSETS_SOURCE_DIR) -o $(ASSETS_COMPILED_DIR)
@$(ASSETS_COMPILLER) icons "$(ASSETS_SOURCE_DIR)" "$(ASSETS_COMPILED_DIR)"
clean:
@echo "\tCLEAN\t"

View File

@ -56,7 +56,7 @@ _Min_Stack_Size = 0x400; /* required amount of stack */
MEMORY
{
FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 32K
RAM1 (xrw) : ORIGIN = 0x20000004, LENGTH = 0x2FFFC
RAM1 (xrw) : ORIGIN = 0x20000008, LENGTH = 0x2FFF8
RAM_SHARED (xrw) : ORIGIN = 0x20030000, LENGTH = 10K
}

View File

@ -0,0 +1,189 @@
#include <furi-hal-gpio.h>
#include <stddef.h>
#include <assert.h>
#define GET_SYSCFG_EXTI_PORT(gpio) \
(((gpio) == (GPIOA)) ? LL_SYSCFG_EXTI_PORTA : \
((gpio) == (GPIOB)) ? LL_SYSCFG_EXTI_PORTB : \
((gpio) == (GPIOC)) ? LL_SYSCFG_EXTI_PORTC : \
((gpio) == (GPIOD)) ? LL_SYSCFG_EXTI_PORTD : \
((gpio) == (GPIOE)) ? LL_SYSCFG_EXTI_PORTE : \
LL_SYSCFG_EXTI_PORTH)
#define GPIO_PIN_MAP(pin, prefix) \
(((pin) == (LL_GPIO_PIN_0)) ? prefix##0 : \
((pin) == (LL_GPIO_PIN_1)) ? prefix##1 : \
((pin) == (LL_GPIO_PIN_2)) ? prefix##2 : \
((pin) == (LL_GPIO_PIN_3)) ? prefix##3 : \
((pin) == (LL_GPIO_PIN_4)) ? prefix##4 : \
((pin) == (LL_GPIO_PIN_5)) ? prefix##5 : \
((pin) == (LL_GPIO_PIN_6)) ? prefix##6 : \
((pin) == (LL_GPIO_PIN_7)) ? prefix##7 : \
((pin) == (LL_GPIO_PIN_8)) ? prefix##8 : \
((pin) == (LL_GPIO_PIN_9)) ? prefix##9 : \
((pin) == (LL_GPIO_PIN_10)) ? prefix##10 : \
((pin) == (LL_GPIO_PIN_11)) ? prefix##11 : \
((pin) == (LL_GPIO_PIN_12)) ? prefix##12 : \
((pin) == (LL_GPIO_PIN_13)) ? prefix##13 : \
((pin) == (LL_GPIO_PIN_14)) ? prefix##14 : \
prefix##15)
#define GET_SYSCFG_EXTI_LINE(pin) GPIO_PIN_MAP(pin, LL_SYSCFG_EXTI_LINE)
#define GET_EXTI_LINE(pin) GPIO_PIN_MAP(pin, LL_EXTI_LINE_)
static volatile GpioInterrupt gpio_interrupt[GPIO_NUMBER];
static uint8_t hal_gpio_get_pin_num(const GpioPin* gpio) {
uint8_t pin_num = 0;
for(pin_num = 0; pin_num < GPIO_NUMBER; pin_num++) {
if(gpio->pin & (1 << pin_num)) break;
}
return pin_num;
}
void hal_gpio_init_simple(const GpioPin* gpio, const GpioMode mode) {
hal_gpio_init(gpio, mode, GpioPullNo, GpioSpeedLow);
}
void hal_gpio_init(
const GpioPin* gpio,
const GpioMode mode,
const GpioPull pull,
const GpioSpeed speed) {
// we cannot set alternate mode in this function
assert(mode != GpioModeAltFunctionPushPull);
assert(mode != GpioModeAltFunctionOpenDrain);
hal_gpio_init_ex(gpio, mode, pull, speed, GpioAltFnUnused);
}
void hal_gpio_init_ex(
const GpioPin* gpio,
const GpioMode mode,
const GpioPull pull,
const GpioSpeed speed,
const GpioAltFn alt_fn) {
uint32_t sys_exti_port = GET_SYSCFG_EXTI_PORT(gpio->port);
uint32_t sys_exti_line = GET_SYSCFG_EXTI_LINE(gpio->pin);
uint32_t exti_line = GET_EXTI_LINE(gpio->pin);
// Configure gpio with interrupts disabled
__disable_irq();
// Set gpio speed
if(speed == GpioSpeedLow) {
LL_GPIO_SetPinSpeed(gpio->port, gpio->pin, LL_GPIO_SPEED_FREQ_LOW);
} else if(speed == GpioSpeedMedium) {
LL_GPIO_SetPinSpeed(gpio->port, gpio->pin, LL_GPIO_SPEED_FREQ_MEDIUM);
} else if(speed == GpioSpeedHigh) {
LL_GPIO_SetPinSpeed(gpio->port, gpio->pin, LL_GPIO_SPEED_FREQ_HIGH);
} else {
LL_GPIO_SetPinSpeed(gpio->port, gpio->pin, LL_GPIO_SPEED_FREQ_VERY_HIGH);
}
// Set gpio pull mode
if(pull == GpioPullNo) {
LL_GPIO_SetPinPull(gpio->port, gpio->pin, LL_GPIO_PULL_NO);
} else if(pull == GpioPullUp) {
LL_GPIO_SetPinPull(gpio->port, gpio->pin, LL_GPIO_PULL_UP);
} else {
LL_GPIO_SetPinPull(gpio->port, gpio->pin, LL_GPIO_PULL_DOWN);
}
// Set gpio mode
if(mode >= GpioModeInterruptRise) {
// Set pin in interrupt mode
LL_GPIO_SetPinMode(gpio->port, gpio->pin, LL_GPIO_MODE_INPUT);
LL_SYSCFG_SetEXTISource(sys_exti_port, sys_exti_line);
if(mode == GpioModeInterruptRise || mode == GpioModeInterruptRiseFall) {
LL_EXTI_EnableIT_0_31(exti_line);
LL_EXTI_EnableRisingTrig_0_31(exti_line);
}
if(mode == GpioModeInterruptFall || mode == GpioModeInterruptRiseFall) {
LL_EXTI_EnableIT_0_31(exti_line);
LL_EXTI_EnableFallingTrig_0_31(exti_line);
}
if(mode == GpioModeEventRise || mode == GpioModeInterruptRiseFall) {
LL_EXTI_EnableEvent_0_31(exti_line);
LL_EXTI_EnableRisingTrig_0_31(exti_line);
}
if(mode == GpioModeEventFall || mode == GpioModeInterruptRiseFall) {
LL_EXTI_EnableEvent_0_31(exti_line);
LL_EXTI_EnableFallingTrig_0_31(exti_line);
}
} else {
// Disable interrupt if it was set
if(LL_SYSCFG_GetEXTISource(sys_exti_line) == sys_exti_port &&
LL_EXTI_IsEnabledIT_0_31(exti_line)) {
LL_EXTI_DisableIT_0_31(exti_line);
LL_EXTI_DisableRisingTrig_0_31(exti_line);
LL_EXTI_DisableFallingTrig_0_31(exti_line);
}
// Set not interrupt pin modes
if(mode == GpioModeInput) {
LL_GPIO_SetPinMode(gpio->port, gpio->pin, LL_GPIO_MODE_INPUT);
} else if(mode == GpioModeOutputPushPull || mode == GpioModeAltFunctionPushPull) {
LL_GPIO_SetPinMode(gpio->port, gpio->pin, LL_GPIO_MODE_OUTPUT);
LL_GPIO_SetPinOutputType(gpio->port, gpio->pin, LL_GPIO_OUTPUT_PUSHPULL);
} else if(mode == GpioModeOutputOpenDrain || mode == GpioModeAltFunctionOpenDrain) {
LL_GPIO_SetPinMode(gpio->port, gpio->pin, LL_GPIO_MODE_OUTPUT);
LL_GPIO_SetPinOutputType(gpio->port, gpio->pin, LL_GPIO_OUTPUT_OPENDRAIN);
} else if(mode == GpioModeAnalog) {
LL_GPIO_SetPinMode(gpio->port, gpio->pin, LL_GPIO_MODE_ANALOG);
}
}
if(mode == GpioModeAltFunctionPushPull || mode == GpioModeAltFunctionOpenDrain) {
// enable alternate mode
LL_GPIO_SetPinMode(gpio->port, gpio->pin, LL_GPIO_MODE_ALTERNATE);
// set alternate function
if(hal_gpio_get_pin_num(gpio) < 8) {
LL_GPIO_SetAFPin_0_7(gpio->port, gpio->pin, alt_fn);
} else {
LL_GPIO_SetAFPin_8_15(gpio->port, gpio->pin, alt_fn);
}
}
__enable_irq();
}
void hal_gpio_add_int_callback(const GpioPin* gpio, GpioExtiCallback cb, void* ctx) {
assert(gpio);
assert(cb);
__disable_irq();
uint8_t pin_num = hal_gpio_get_pin_num(gpio);
gpio_interrupt[pin_num].callback = cb;
gpio_interrupt[pin_num].context = ctx;
gpio_interrupt[pin_num].ready = true;
__enable_irq();
}
void hal_gpio_enable_int_callback(const GpioPin* gpio) {
assert(gpio);
__disable_irq();
uint8_t pin_num = hal_gpio_get_pin_num(gpio);
if(gpio_interrupt[pin_num].callback) {
gpio_interrupt[pin_num].ready = true;
}
__enable_irq();
}
void hal_gpio_disable_int_callback(const GpioPin* gpio) {
assert(gpio);
__disable_irq();
uint8_t pin_num = hal_gpio_get_pin_num(gpio);
gpio_interrupt[pin_num].ready = false;
__enable_irq();
}
void hal_gpio_remove_int_callback(const GpioPin* gpio) {
assert(gpio);
__disable_irq();
uint8_t pin_num = hal_gpio_get_pin_num(gpio);
gpio_interrupt[pin_num].callback = NULL;
gpio_interrupt[pin_num].context = NULL;
gpio_interrupt[pin_num].ready = false;
__enable_irq();
}

View File

@ -0,0 +1,264 @@
#pragma once
#include "main.h"
#include "stdbool.h"
#include <stm32wbxx_ll_gpio.h>
#include <stm32wbxx_ll_system.h>
#include <stm32wbxx_ll_exti.h>
#ifdef __cplusplus
extern "C" {
#endif
/**
* Number of gpio on one port
*/
#define GPIO_NUMBER (16U)
/**
* Interrupt callback prototype
*/
typedef void (*GpioExtiCallback)(void* ctx);
/**
* Gpio interrupt type
*/
typedef struct {
GpioExtiCallback callback;
void* context;
volatile bool ready;
} GpioInterrupt;
/**
* Gpio modes
*/
typedef enum {
GpioModeInput,
GpioModeOutputPushPull,
GpioModeOutputOpenDrain,
GpioModeAltFunctionPushPull,
GpioModeAltFunctionOpenDrain,
GpioModeAnalog,
GpioModeInterruptRise,
GpioModeInterruptFall,
GpioModeInterruptRiseFall,
GpioModeEventRise,
GpioModeEventFall,
GpioModeEventRiseFall,
} GpioMode;
/**
* Gpio pull modes
*/
typedef enum {
GpioPullNo,
GpioPullUp,
GpioPullDown,
} GpioPull;
/**
* Gpio speed modes
*/
typedef enum {
GpioSpeedLow,
GpioSpeedMedium,
GpioSpeedHigh,
GpioSpeedVeryHigh,
} GpioSpeed;
/**
* Gpio alternate functions
*/
typedef enum {
GpioAltFn0MCO = 0, /*!< MCO Alternate Function mapping */
GpioAltFn0LSCO = 0, /*!< LSCO Alternate Function mapping */
GpioAltFn0JTMS_SWDIO = 0, /*!< JTMS-SWDIO Alternate Function mapping */
GpioAltFn0JTCK_SWCLK = 0, /*!< JTCK-SWCLK Alternate Function mapping */
GpioAltFn0JTDI = 0, /*!< JTDI Alternate Function mapping */
GpioAltFn0RTC_OUT = 0, /*!< RCT_OUT Alternate Function mapping */
GpioAltFn0JTD_TRACE = 0, /*!< JTDO-TRACESWO Alternate Function mapping */
GpioAltFn0NJTRST = 0, /*!< NJTRST Alternate Function mapping */
GpioAltFn0RTC_REFIN = 0, /*!< RTC_REFIN Alternate Function mapping */
GpioAltFn0TRACED0 = 0, /*!< TRACED0 Alternate Function mapping */
GpioAltFn0TRACED1 = 0, /*!< TRACED1 Alternate Function mapping */
GpioAltFn0TRACED2 = 0, /*!< TRACED2 Alternate Function mapping */
GpioAltFn0TRACED3 = 0, /*!< TRACED3 Alternate Function mapping */
GpioAltFn0TRIG_INOUT = 0, /*!< TRIG_INOUT Alternate Function mapping */
GpioAltFn0TRACECK = 0, /*!< TRACECK Alternate Function mapping */
GpioAltFn0SYS = 0, /*!< System Function mapping */
GpioAltFn1TIM1 = 1, /*!< TIM1 Alternate Function mapping */
GpioAltFn1TIM2 = 1, /*!< TIM2 Alternate Function mapping */
GpioAltFn1LPTIM1 = 1, /*!< LPTIM1 Alternate Function mapping */
GpioAltFn2TIM2 = 2, /*!< TIM2 Alternate Function mapping */
GpioAltFn2TIM1 = 2, /*!< TIM1 Alternate Function mapping */
GpioAltFn3SAI1 = 3, /*!< SAI1_CK1 Alternate Function mapping */
GpioAltFn3SPI2 = 3, /*!< SPI2 Alternate Function mapping */
GpioAltFn3TIM1 = 3, /*!< TIM1 Alternate Function mapping */
GpioAltFn4I2C1 = 4, /*!< I2C1 Alternate Function mapping */
GpioAltFn4I2C3 = 4, /*!< I2C3 Alternate Function mapping */
GpioAltFn5SPI1 = 5, /*!< SPI1 Alternate Function mapping */
GpioAltFn5SPI2 = 5, /*!< SPI2 Alternate Function mapping */
GpioAltFn6MCO = 6, /*!< MCO Alternate Function mapping */
GpioAltFn6LSCO = 6, /*!< LSCO Alternate Function mapping */
GpioAltFn6RF_DTB0 = 6, /*!< RF_DTB0 Alternate Function mapping */
GpioAltFn6RF_DTB1 = 6, /*!< RF_DTB1 Alternate Function mapping */
GpioAltFn6RF_DTB2 = 6, /*!< RF_DTB2 Alternate Function mapping */
GpioAltFn6RF_DTB3 = 6, /*!< RF_DTB3 Alternate Function mapping */
GpioAltFn6RF_DTB4 = 6, /*!< RF_DTB4 Alternate Function mapping */
GpioAltFn6RF_DTB5 = 6, /*!< RF_DTB5 Alternate Function mapping */
GpioAltFn6RF_DTB6 = 6, /*!< RF_DTB6 Alternate Function mapping */
GpioAltFn6RF_DTB7 = 6, /*!< RF_DTB7 Alternate Function mapping */
GpioAltFn6RF_DTB8 = 6, /*!< RF_DTB8 Alternate Function mapping */
GpioAltFn6RF_DTB9 = 6, /*!< RF_DTB9 Alternate Function mapping */
GpioAltFn6RF_DTB10 = 6, /*!< RF_DTB10 Alternate Function mapping */
GpioAltFn6RF_DTB11 = 6, /*!< RF_DTB11 Alternate Function mapping */
GpioAltFn6RF_DTB12 = 6, /*!< RF_DTB12 Alternate Function mapping */
GpioAltFn6RF_DTB13 = 6, /*!< RF_DTB13 Alternate Function mapping */
GpioAltFn6RF_DTB14 = 6, /*!< RF_DTB14 Alternate Function mapping */
GpioAltFn6RF_DTB15 = 6, /*!< RF_DTB15 Alternate Function mapping */
GpioAltFn6RF_DTB16 = 6, /*!< RF_DTB16 Alternate Function mapping */
GpioAltFn6RF_DTB17 = 6, /*!< RF_DTB17 Alternate Function mapping */
GpioAltFn6RF_DTB18 = 6, /*!< RF_DTB18 Alternate Function mapping */
GpioAltFn6RF_MISO = 6, /*!< RF_MISO Alternate Function mapping */
GpioAltFn6RF_MOSI = 6, /*!< RF_MOSI Alternate Function mapping */
GpioAltFn6RF_SCK = 6, /*!< RF_SCK Alternate Function mapping */
GpioAltFn6RF_NSS = 6, /*!< RF_NSS Alternate Function mapping */
GpioAltFn7USART1 = 7, /*!< USART1 Alternate Function mapping */
GpioAltFn8LPUART1 = 8, /*!< LPUART1 Alternate Function mapping */
GpioAltFn8IR = 8, /*!< IR Alternate Function mapping */
GpioAltFn9TSC = 9, /*!< TSC Alternate Function mapping */
GpioAltFn10QUADSPI = 10, /*!< QUADSPI Alternate Function mapping */
GpioAltFn10USB = 10, /*!< USB Alternate Function mapping */
GpioAltFn11LCD = 11, /*!< LCD Alternate Function mapping */
GpioAltFn12COMP1 = 12, /*!< COMP1 Alternate Function mapping */
GpioAltFn12COMP2 = 12, /*!< COMP2 Alternate Function mapping */
GpioAltFn12TIM1 = 12, /*!< TIM1 Alternate Function mapping */
GpioAltFn13SAI1 = 13, /*!< SAI1 Alternate Function mapping */
GpioAltFn14TIM2 = 14, /*!< TIM2 Alternate Function mapping */
GpioAltFn14TIM16 = 14, /*!< TIM16 Alternate Function mapping */
GpioAltFn14TIM17 = 14, /*!< TIM17 Alternate Function mapping */
GpioAltFn14LPTIM2 = 14, /*!< LPTIM2 Alternate Function mapping */
GpioAltFn15EVENTOUT = 15, /*!< EVENTOUT Alternate Function mapping */
GpioAltFnUnused = 16, /*!< just dummy value */
} GpioAltFn;
/**
* Gpio structure
*/
typedef struct {
GPIO_TypeDef* port;
uint16_t pin;
} GpioPin;
/**
* GPIO initialization function, simple version
* @param gpio GpioPin
* @param mode GpioMode
*/
void hal_gpio_init_simple(const GpioPin* gpio, const GpioMode mode);
/**
* GPIO initialization function, normal version
* @param gpio GpioPin
* @param mode GpioMode
* @param pull GpioPull
* @param speed GpioSpeed
*/
void hal_gpio_init(
const GpioPin* gpio,
const GpioMode mode,
const GpioPull pull,
const GpioSpeed speed);
/**
* GPIO initialization function, extended version
* @param gpio GpioPin
* @param mode GpioMode
* @param pull GpioPull
* @param speed GpioSpeed
* @param alt_fn GpioAltFn
*/
void hal_gpio_init_ex(
const GpioPin* gpio,
const GpioMode mode,
const GpioPull pull,
const GpioSpeed speed,
const GpioAltFn alt_fn);
/**
* Add and enable interrupt
* @param gpio GpioPin
* @param cb GpioExtiCallback
* @param ctx context for callback
*/
void hal_gpio_add_int_callback(const GpioPin* gpio, GpioExtiCallback cb, void* ctx);
/**
* Enable interrupt
* @param gpio GpioPin
*/
void hal_gpio_enable_int_callback(const GpioPin* gpio);
/**
* Disable interrupt
* @param gpio GpioPin
*/
void hal_gpio_disable_int_callback(const GpioPin* gpio);
/**
* Remove interrupt
* @param gpio GpioPin
*/
void hal_gpio_remove_int_callback(const GpioPin* gpio);
/**
* GPIO write pin
* @param gpio GpioPin
* @param state true / false
*/
static inline void hal_gpio_write(const GpioPin* gpio, const bool state) {
// writing to BSSR is an atomic operation
if(state == true) {
gpio->port->BSRR = gpio->pin;
} else {
gpio->port->BSRR = (uint32_t)gpio->pin << GPIO_NUMBER;
}
}
/**
* GPIO read pin
* @param gpio GpioPin
* @return true / false
*/
static inline bool hal_gpio_read(const GpioPin* gpio) {
if((gpio->port->IDR & gpio->pin) != 0x00U) {
return true;
} else {
return false;
}
}
/**
* Get RFID IN level
* @return false = LOW, true = HIGH
*/
bool hal_gpio_get_rfid_in_level();
#ifdef __cplusplus
}
#endif

View File

@ -0,0 +1,137 @@
#include <furi-hal-i2c.h>
#include <stm32wbxx_ll_bus.h>
#include <stm32wbxx_ll_i2c.h>
#include <stm32wbxx_ll_rcc.h>
#include <stm32wbxx_ll_gpio.h>
#include <stm32wbxx_ll_cortex.h>
void furi_hal_i2c_init() {
LL_I2C_InitTypeDef I2C_InitStruct = {0};
LL_GPIO_InitTypeDef GPIO_InitStruct = {0};
LL_RCC_SetI2CClockSource(LL_RCC_I2C1_CLKSOURCE_PCLK1);
LL_AHB2_GRP1_EnableClock(LL_AHB2_GRP1_PERIPH_GPIOA);
GPIO_InitStruct.Pin = POWER_I2C_SCL_Pin | POWER_I2C_SDA_Pin;
GPIO_InitStruct.Mode = LL_GPIO_MODE_ALTERNATE;
GPIO_InitStruct.Speed = LL_GPIO_SPEED_FREQ_LOW;
GPIO_InitStruct.OutputType = LL_GPIO_OUTPUT_OPENDRAIN;
GPIO_InitStruct.Pull = LL_GPIO_PULL_NO;
GPIO_InitStruct.Alternate = LL_GPIO_AF_4;
LL_GPIO_Init(GPIOA, &GPIO_InitStruct);
LL_APB1_GRP1_EnableClock(LL_APB1_GRP1_PERIPH_I2C1);
I2C_InitStruct.PeripheralMode = LL_I2C_MODE_I2C;
I2C_InitStruct.Timing = POWER_I2C_TIMINGS;
I2C_InitStruct.AnalogFilter = LL_I2C_ANALOGFILTER_ENABLE;
I2C_InitStruct.DigitalFilter = 0;
I2C_InitStruct.OwnAddress1 = 0;
I2C_InitStruct.TypeAcknowledge = LL_I2C_ACK;
I2C_InitStruct.OwnAddrSize = LL_I2C_OWNADDRESS1_7BIT;
LL_I2C_Init(I2C1, &I2C_InitStruct);
LL_I2C_EnableAutoEndMode(I2C1);
LL_I2C_SetOwnAddress2(I2C1, 0, LL_I2C_OWNADDRESS2_NOMASK);
LL_I2C_DisableOwnAddress2(I2C1);
LL_I2C_DisableGeneralCall(I2C1);
LL_I2C_EnableClockStretching(I2C1);
}
bool furi_hal_i2c_tx(
I2C_TypeDef* instance,
uint8_t address,
const uint8_t* data,
uint8_t size,
uint32_t timeout) {
uint32_t time_left = timeout;
bool ret = true;
while(LL_I2C_IsActiveFlag_BUSY(instance))
;
LL_I2C_HandleTransfer(
instance,
address,
LL_I2C_ADDRSLAVE_7BIT,
size,
LL_I2C_MODE_AUTOEND,
LL_I2C_GENERATE_START_WRITE);
while(!LL_I2C_IsActiveFlag_STOP(instance) || size > 0) {
if(LL_I2C_IsActiveFlag_TXIS(instance)) {
LL_I2C_TransmitData8(instance, (*data));
data++;
size--;
time_left = timeout;
}
if(LL_SYSTICK_IsActiveCounterFlag()) {
if(--time_left == 0) {
ret = false;
break;
}
}
}
LL_I2C_ClearFlag_STOP(instance);
return ret;
}
bool furi_hal_i2c_rx(
I2C_TypeDef* instance,
uint8_t address,
uint8_t* data,
uint8_t size,
uint32_t timeout) {
uint32_t time_left = timeout;
bool ret = true;
while(LL_I2C_IsActiveFlag_BUSY(instance))
;
LL_I2C_HandleTransfer(
instance,
address,
LL_I2C_ADDRSLAVE_7BIT,
size,
LL_I2C_MODE_AUTOEND,
LL_I2C_GENERATE_START_READ);
while(!LL_I2C_IsActiveFlag_STOP(instance) || size > 0) {
if(LL_I2C_IsActiveFlag_RXNE(instance)) {
*data = LL_I2C_ReceiveData8(instance);
data++;
size--;
time_left = timeout;
}
if(LL_SYSTICK_IsActiveCounterFlag()) {
if(--time_left == 0) {
ret = false;
break;
}
}
}
LL_I2C_ClearFlag_STOP(instance);
return ret;
}
bool furi_hal_i2c_trx(
I2C_TypeDef* instance,
uint8_t address,
const uint8_t* tx_data,
uint8_t tx_size,
uint8_t* rx_data,
uint8_t rx_size,
uint32_t timeout) {
if(furi_hal_i2c_tx(instance, address, tx_data, tx_size, timeout) &&
furi_hal_i2c_rx(instance, address, rx_data, rx_size, timeout)) {
return true;
} else {
return false;
}
}

View File

@ -0,0 +1,43 @@
#pragma once
#include <stdbool.h>
#include <stdint.h>
#include <furi-hal-resources.h>
#ifdef __cplusplus
extern "C" {
#endif
void furi_hal_i2c_init();
bool furi_hal_i2c_tx(
I2C_TypeDef* instance,
const uint8_t address,
const uint8_t* data,
const uint8_t size,
uint32_t timeout);
bool furi_hal_i2c_rx(
I2C_TypeDef* instance,
const uint8_t address,
uint8_t* data,
const uint8_t size,
uint32_t timeout);
bool furi_hal_i2c_trx(
I2C_TypeDef* instance,
const uint8_t address,
const uint8_t* tx_data,
const uint8_t tx_size,
uint8_t* rx_data,
const uint8_t rx_size,
uint32_t timeout);
#define with_furi_hal_i2c(type, pointer, function_body) \
{ \
*pointer = ({ type __fn__ function_body __fn__; })(); \
}
#ifdef __cplusplus
}
#endif

View File

@ -0,0 +1,43 @@
#include <furi-hal-light.h>
#include <lp5562.h>
#define LED_CURRENT_RED 50
#define LED_CURRENT_GREEN 50
#define LED_CURRENT_BLUE 50
#define LED_CURRENT_WHITE 150
void furi_hal_light_init() {
lp5562_reset();
lp5562_set_channel_current(LP5562ChannelRed, LED_CURRENT_RED);
lp5562_set_channel_current(LP5562ChannelGreen, LED_CURRENT_GREEN);
lp5562_set_channel_current(LP5562ChannelBlue, LED_CURRENT_BLUE);
lp5562_set_channel_current(LP5562ChannelWhite, LED_CURRENT_WHITE);
lp5562_set_channel_value(LP5562ChannelRed, 0x00);
lp5562_set_channel_value(LP5562ChannelGreen, 0x00);
lp5562_set_channel_value(LP5562ChannelBlue, 0x00);
lp5562_set_channel_value(LP5562ChannelWhite, 0x00);
lp5562_enable();
lp5562_configure();
}
void furi_hal_light_set(Light light, uint8_t value) {
switch(light) {
case LightRed:
lp5562_set_channel_value(LP5562ChannelRed, value);
break;
case LightGreen:
lp5562_set_channel_value(LP5562ChannelGreen, value);
break;
case LightBlue:
lp5562_set_channel_value(LP5562ChannelBlue, value);
break;
case LightBacklight:
lp5562_set_channel_value(LP5562ChannelWhite, value);
break;
default:
break;
}
}

View File

@ -0,0 +1,17 @@
#pragma once
#include <stdbool.h>
#include <stdint.h>
#include <furi-hal-resources.h>
#ifdef __cplusplus
extern "C" {
#endif
void furi_hal_light_init();
void furi_hal_light_set(Light light, uint8_t value);
#ifdef __cplusplus
}
#endif

View File

@ -0,0 +1,41 @@
#include "furi-hal-resources.h"
#include "main.h"
const GpioPin vibro_gpio = {.port = VIBRO_GPIO_Port, .pin = VIBRO_Pin};
const GpioPin ibutton_gpio = {.port = iBTN_GPIO_Port, .pin = iBTN_Pin};
const GpioPin gpio_cc1101_g0 = {.port = CC1101_G0_GPIO_Port, .pin = CC1101_G0_Pin};
const GpioPin gpio_rf_sw_0 = {.port = RF_SW_0_GPIO_Port, .pin = RF_SW_0_Pin};
const GpioPin gpio_subghz_cs = {.port = CC1101_CS_GPIO_Port, .pin = CC1101_CS_Pin};
const GpioPin gpio_display_cs = {.port = DISPLAY_CS_GPIO_Port, .pin = DISPLAY_CS_Pin};
const GpioPin gpio_display_rst = {.port = DISPLAY_RST_GPIO_Port, .pin = DISPLAY_RST_Pin};
const GpioPin gpio_display_di = {.port = DISPLAY_DI_GPIO_Port, .pin = DISPLAY_DI_Pin};
const GpioPin gpio_sdcard_cs = {.port = SD_CS_GPIO_Port, .pin = SD_CS_Pin};
const GpioPin gpio_nfc_cs = {.port = NFC_CS_GPIO_Port, .pin = NFC_CS_Pin};
const GpioPin gpio_spi_d_miso = {.port = SPI_D_MISO_GPIO_Port, .pin = SPI_D_MISO_Pin};
const GpioPin gpio_spi_d_mosi = {.port = SPI_D_MOSI_GPIO_Port, .pin = SPI_D_MOSI_Pin};
const GpioPin gpio_spi_d_sck = {.port = SPI_D_SCK_GPIO_Port, .pin = SPI_D_SCK_Pin};
const GpioPin gpio_spi_r_miso = {.port = SPI_R_MISO_GPIO_Port, .pin = SPI_R_MISO_Pin};
const GpioPin gpio_spi_r_mosi = {.port = SPI_R_MOSI_GPIO_Port, .pin = SPI_R_MOSI_Pin};
const GpioPin gpio_spi_r_sck = {.port = SPI_R_SCK_GPIO_Port, .pin = SPI_R_SCK_Pin};
const GpioPin gpio_ext_pc0 = {.port = GPIOC, .pin = LL_GPIO_PIN_0};
const GpioPin gpio_ext_pc1 = {.port = GPIOC, .pin = LL_GPIO_PIN_1};
const GpioPin gpio_ext_pc3 = {.port = GPIOC, .pin = LL_GPIO_PIN_3};
const GpioPin gpio_ext_pb2 = {.port = GPIOB, .pin = LL_GPIO_PIN_2};
const GpioPin gpio_ext_pb3 = {.port = GPIOB, .pin = LL_GPIO_PIN_3};
const GpioPin gpio_ext_pa4 = {.port = GPIOA, .pin = LL_GPIO_PIN_4};
const GpioPin gpio_ext_pa6 = {.port = GPIOA, .pin = LL_GPIO_PIN_6};
const GpioPin gpio_ext_pa7 = {.port = GPIOA, .pin = LL_GPIO_PIN_7};
const GpioPin gpio_rfid_pull = {.port = RFID_PULL_GPIO_Port, .pin = RFID_PULL_Pin};
const GpioPin gpio_rfid_carrier_out = {.port = RFID_OUT_GPIO_Port, .pin = RFID_OUT_Pin};
const GpioPin gpio_rfid_data_in = {.port = RFID_RF_IN_GPIO_Port, .pin = RFID_RF_IN_Pin};
const GpioPin gpio_irda_rx = {.port = IR_RX_GPIO_Port, .pin = IR_RX_Pin};
const GpioPin gpio_irda_tx = {.port = IR_TX_GPIO_Port, .pin = IR_TX_Pin};
const GpioPin gpio_usart_tx = {.port = USART1_TX_Port, .pin = USART1_TX_Pin};
const GpioPin gpio_usart_rx = {.port = USART1_RX_Port, .pin = USART1_RX_Pin};

View File

@ -0,0 +1,82 @@
#pragma once
#include <stm32wbxx.h>
#include <stm32wbxx_ll_gpio.h>
#include <furi-hal-gpio.h>
#ifdef __cplusplus
extern "C" {
#endif
#define POWER_I2C_SCL_Pin LL_GPIO_PIN_9
#define POWER_I2C_SCL_GPIO_Port GPIOA
#define POWER_I2C_SDA_Pin LL_GPIO_PIN_10
#define POWER_I2C_SDA_GPIO_Port GPIOA
#define POWER_I2C I2C1
/* Timing register value is computed with the STM32CubeMX Tool,
* Fast Mode @100kHz with I2CCLK = 64 MHz,
* rise time = 0ns, fall time = 0ns
*/
#define POWER_I2C_TIMINGS 0x10707DBC
/* Input Keys */
typedef enum {
InputKeyUp,
InputKeyDown,
InputKeyRight,
InputKeyLeft,
InputKeyOk,
InputKeyBack,
} InputKey;
/* Light */
typedef enum {
LightRed,
LightGreen,
LightBlue,
LightBacklight,
} Light;
extern const GpioPin vibro_gpio;
extern const GpioPin ibutton_gpio;
extern const GpioPin gpio_cc1101_g0;
extern const GpioPin gpio_rf_sw_0;
extern const GpioPin gpio_subghz_cs;
extern const GpioPin gpio_display_cs;
extern const GpioPin gpio_display_rst;
extern const GpioPin gpio_display_di;
extern const GpioPin gpio_sdcard_cs;
extern const GpioPin gpio_nfc_cs;
extern const GpioPin gpio_spi_d_miso;
extern const GpioPin gpio_spi_d_mosi;
extern const GpioPin gpio_spi_d_sck;
extern const GpioPin gpio_spi_r_miso;
extern const GpioPin gpio_spi_r_mosi;
extern const GpioPin gpio_spi_r_sck;
extern const GpioPin gpio_ext_pc0;
extern const GpioPin gpio_ext_pc1;
extern const GpioPin gpio_ext_pc3;
extern const GpioPin gpio_ext_pb2;
extern const GpioPin gpio_ext_pb3;
extern const GpioPin gpio_ext_pa4;
extern const GpioPin gpio_ext_pa6;
extern const GpioPin gpio_ext_pa7;
extern const GpioPin gpio_rfid_pull;
extern const GpioPin gpio_rfid_carrier_out;
extern const GpioPin gpio_rfid_data_in;
extern const GpioPin gpio_irda_rx;
extern const GpioPin gpio_irda_tx;
extern const GpioPin gpio_usart_tx;
extern const GpioPin gpio_usart_rx;
#ifdef __cplusplus
}
#endif

View File

@ -0,0 +1,114 @@
#include <furi-hal-spi-config.h>
#include <furi-hal-resources.h>
#define SPI_R SPI1
#define SPI_D SPI2
const LL_SPI_InitTypeDef furi_hal_spi_config_nfc = {
.Mode = LL_SPI_MODE_MASTER,
.TransferDirection = LL_SPI_FULL_DUPLEX,
.DataWidth = LL_SPI_DATAWIDTH_8BIT,
.ClockPolarity = LL_SPI_POLARITY_LOW,
.ClockPhase = LL_SPI_PHASE_2EDGE,
.NSS = LL_SPI_NSS_SOFT,
.BaudRate = LL_SPI_BAUDRATEPRESCALER_DIV8,
.BitOrder = LL_SPI_MSB_FIRST,
.CRCCalculation = LL_SPI_CRCCALCULATION_DISABLE,
.CRCPoly = 7,
};
const LL_SPI_InitTypeDef furi_hal_spi_config_subghz = {
.Mode = LL_SPI_MODE_MASTER,
.TransferDirection = LL_SPI_FULL_DUPLEX,
.DataWidth = LL_SPI_DATAWIDTH_8BIT,
.ClockPolarity = LL_SPI_POLARITY_LOW,
.ClockPhase = LL_SPI_PHASE_1EDGE,
.NSS = LL_SPI_NSS_SOFT,
.BaudRate = LL_SPI_BAUDRATEPRESCALER_DIV8,
.BitOrder = LL_SPI_MSB_FIRST,
.CRCCalculation = LL_SPI_CRCCALCULATION_DISABLE,
.CRCPoly = 7,
};
const LL_SPI_InitTypeDef furi_hal_spi_config_display = {
.Mode = LL_SPI_MODE_MASTER,
.TransferDirection = LL_SPI_FULL_DUPLEX,
.DataWidth = LL_SPI_DATAWIDTH_8BIT,
.ClockPolarity = LL_SPI_POLARITY_LOW,
.ClockPhase = LL_SPI_PHASE_1EDGE,
.NSS = LL_SPI_NSS_SOFT,
.BaudRate = LL_SPI_BAUDRATEPRESCALER_DIV16,
.BitOrder = LL_SPI_MSB_FIRST,
.CRCCalculation = LL_SPI_CRCCALCULATION_DISABLE,
.CRCPoly = 7,
};
/**
* SD Card in fast mode (after init)
*/
const LL_SPI_InitTypeDef furi_hal_spi_config_sd_fast = {
.Mode = LL_SPI_MODE_MASTER,
.TransferDirection = LL_SPI_FULL_DUPLEX,
.DataWidth = LL_SPI_DATAWIDTH_8BIT,
.ClockPolarity = LL_SPI_POLARITY_LOW,
.ClockPhase = LL_SPI_PHASE_1EDGE,
.NSS = LL_SPI_NSS_SOFT,
.BaudRate = LL_SPI_BAUDRATEPRESCALER_DIV2,
.BitOrder = LL_SPI_MSB_FIRST,
.CRCCalculation = LL_SPI_CRCCALCULATION_DISABLE,
.CRCPoly = 7,
};
/**
* SD Card in slow mode (before init)
*/
const LL_SPI_InitTypeDef furi_hal_spi_config_sd_slow = {
.Mode = LL_SPI_MODE_MASTER,
.TransferDirection = LL_SPI_FULL_DUPLEX,
.DataWidth = LL_SPI_DATAWIDTH_8BIT,
.ClockPolarity = LL_SPI_POLARITY_LOW,
.ClockPhase = LL_SPI_PHASE_1EDGE,
.NSS = LL_SPI_NSS_SOFT,
.BaudRate = LL_SPI_BAUDRATEPRESCALER_DIV32,
.BitOrder = LL_SPI_MSB_FIRST,
.CRCCalculation = LL_SPI_CRCCALCULATION_DISABLE,
.CRCPoly = 7,
};
const FuriHalSpiBus spi_r = {
.spi = SPI_R,
.miso = &gpio_spi_r_miso,
.mosi = &gpio_spi_r_mosi,
.clk = &gpio_spi_r_sck,
};
const FuriHalSpiBus spi_d = {
.spi = SPI_D,
.miso = &gpio_spi_d_miso,
.mosi = &gpio_spi_d_mosi,
.clk = &gpio_spi_d_sck,
};
const FuriHalSpiDevice furi_hal_spi_devices[FuriHalSpiDeviceIdMax] = {
{
.bus = &spi_r,
.config = &furi_hal_spi_config_subghz,
.chip_select = &gpio_subghz_cs,
},
{
.bus = &spi_d,
.config = &furi_hal_spi_config_display,
.chip_select = &gpio_display_cs,
},
{
.bus = &spi_d,
.config = &furi_hal_spi_config_sd_fast,
.chip_select = &gpio_sdcard_cs,
},
{
.bus = &spi_d,
.config = &furi_hal_spi_config_sd_slow,
.chip_select = &gpio_sdcard_cs,
},
{.bus = &spi_r, .config = &furi_hal_spi_config_nfc, .chip_select = &gpio_nfc_cs},
};

View File

@ -0,0 +1,61 @@
#pragma once
#include <furi-hal-gpio.h>
#include <stm32wbxx_ll_spi.h>
#ifdef __cplusplus
extern "C" {
#endif
extern const LL_SPI_InitTypeDef furi_hal_spi_config_nfc;
extern const LL_SPI_InitTypeDef furi_hal_spi_config_subghz;
extern const LL_SPI_InitTypeDef furi_hal_spi_config_display;
extern const LL_SPI_InitTypeDef furi_hal_spi_config_sd_fast;
extern const LL_SPI_InitTypeDef furi_hal_spi_config_sd_slow;
/** FURI HAL SPI BUS handler
* Structure content may change at some point
*/
typedef struct {
const SPI_TypeDef* spi;
const GpioPin* miso;
const GpioPin* mosi;
const GpioPin* clk;
} FuriHalSpiBus;
/** FURI HAL SPI Device handler
* Structure content may change at some point
*/
typedef struct {
const FuriHalSpiBus* bus;
const LL_SPI_InitTypeDef* config;
const GpioPin* chip_select;
} FuriHalSpiDevice;
/** FURI HAL SPI Standard Device IDs */
typedef enum {
FuriHalSpiDeviceIdSubGhz, /** SubGhz: CC1101, non-standard SPI usage */
FuriHalSpiDeviceIdDisplay, /** Display: ERC12864, only have MOSI */
FuriHalSpiDeviceIdSdCardFast, /** SDCARD: fast mode, after initialization */
FuriHalSpiDeviceIdSdCardSlow, /** SDCARD: slow mode, before initialization */
FuriHalSpiDeviceIdNfc, /** NFC: ST25R3916, pretty standard, but RFAL makes it complex */
FuriHalSpiDeviceIdMax, /** Service Value, do not use */
} FuriHalSpiDeviceId;
/** Furi Hal Spi Bus R
* CC1101, Nfc
*/
extern const FuriHalSpiBus spi_r;
/** Furi Hal Spi Bus D
* Display, SdCard
*/
extern const FuriHalSpiBus spi_d;
/** Furi Hal Spi devices */
extern const FuriHalSpiDevice furi_hal_spi_devices[FuriHalSpiDeviceIdMax];
#ifdef __cplusplus
}
#endif

View File

@ -0,0 +1,240 @@
#include "furi-hal-spi.h"
#include "furi-hal-resources.h"
#include <stdbool.h>
#include <assert.h>
#include <stm32wbxx_ll_spi.h>
#include <stm32wbxx_ll_utils.h>
#include <stm32wbxx_ll_cortex.h>
extern void Enable_SPI(SPI_TypeDef* spi);
void furi_hal_spi_init() {
for(size_t i = 0; i < FuriHalSpiDeviceIdMax; ++i) {
hal_gpio_write(furi_hal_spi_devices[i].chip_select, true);
hal_gpio_init(
furi_hal_spi_devices[i].chip_select,
GpioModeOutputPushPull,
GpioPullNo,
GpioSpeedVeryHigh);
}
hal_gpio_init_ex(
&gpio_spi_r_miso,
GpioModeAltFunctionPushPull,
GpioPullNo,
GpioSpeedVeryHigh,
GpioAltFn5SPI1);
hal_gpio_init_ex(
&gpio_spi_r_mosi,
GpioModeAltFunctionPushPull,
GpioPullNo,
GpioSpeedVeryHigh,
GpioAltFn5SPI1);
hal_gpio_init_ex(
&gpio_spi_r_sck,
GpioModeAltFunctionPushPull,
GpioPullNo,
GpioSpeedVeryHigh,
GpioAltFn5SPI1);
hal_gpio_init_ex(
&gpio_spi_d_miso,
GpioModeAltFunctionPushPull,
GpioPullUp,
GpioSpeedVeryHigh,
GpioAltFn5SPI2);
hal_gpio_init_ex(
&gpio_spi_d_mosi,
GpioModeAltFunctionPushPull,
GpioPullUp,
GpioSpeedVeryHigh,
GpioAltFn5SPI2);
hal_gpio_init_ex(
&gpio_spi_d_sck,
GpioModeAltFunctionPushPull,
GpioPullUp,
GpioSpeedVeryHigh,
GpioAltFn5SPI2);
}
void furi_hal_spi_bus_lock(const FuriHalSpiBus* bus) {
assert(bus);
}
void furi_hal_spi_bus_unlock(const FuriHalSpiBus* bus) {
assert(bus);
}
void furi_hal_spi_bus_configure(const FuriHalSpiBus* bus, const LL_SPI_InitTypeDef* config) {
assert(bus);
LL_SPI_DeInit((SPI_TypeDef*)bus->spi);
LL_SPI_Init((SPI_TypeDef*)bus->spi, (LL_SPI_InitTypeDef*)config);
LL_SPI_SetRxFIFOThreshold((SPI_TypeDef*)bus->spi, LL_SPI_RX_FIFO_TH_QUARTER);
LL_SPI_Enable((SPI_TypeDef*)bus->spi);
}
void furi_hal_spi_bus_end_txrx(const FuriHalSpiBus* bus, uint32_t timeout) {
while(LL_SPI_GetTxFIFOLevel((SPI_TypeDef*)bus->spi) != LL_SPI_TX_FIFO_EMPTY)
;
while(LL_SPI_IsActiveFlag_BSY((SPI_TypeDef*)bus->spi))
;
while(LL_SPI_GetRxFIFOLevel((SPI_TypeDef*)bus->spi) != LL_SPI_RX_FIFO_EMPTY) {
LL_SPI_ReceiveData8((SPI_TypeDef*)bus->spi);
}
}
bool furi_hal_spi_bus_rx(const FuriHalSpiBus* bus, uint8_t* buffer, size_t size, uint32_t timeout) {
assert(bus);
assert(buffer);
assert(size > 0);
return furi_hal_spi_bus_trx(bus, buffer, buffer, size, timeout);
}
bool furi_hal_spi_bus_tx(const FuriHalSpiBus* bus, uint8_t* buffer, size_t size, uint32_t timeout) {
assert(bus);
assert(buffer);
assert(size > 0);
bool ret = true;
while(size > 0) {
if(LL_SPI_IsActiveFlag_TXE((SPI_TypeDef*)bus->spi)) {
LL_SPI_TransmitData8((SPI_TypeDef*)bus->spi, *buffer);
buffer++;
size--;
}
}
furi_hal_spi_bus_end_txrx(bus, timeout);
LL_SPI_ClearFlag_OVR((SPI_TypeDef*)bus->spi);
return ret;
}
bool furi_hal_spi_bus_trx(
const FuriHalSpiBus* bus,
uint8_t* tx_buffer,
uint8_t* rx_buffer,
size_t size,
uint32_t timeout) {
assert(bus);
assert(tx_buffer);
assert(rx_buffer);
assert(size > 0);
bool ret = true;
size_t tx_size = size;
bool tx_allowed = true;
while(size > 0) {
if(tx_size > 0 && LL_SPI_IsActiveFlag_TXE((SPI_TypeDef*)bus->spi) && tx_allowed) {
LL_SPI_TransmitData8((SPI_TypeDef*)bus->spi, *tx_buffer);
tx_buffer++;
tx_size--;
tx_allowed = false;
}
if(LL_SPI_IsActiveFlag_RXNE((SPI_TypeDef*)bus->spi)) {
*rx_buffer = LL_SPI_ReceiveData8((SPI_TypeDef*)bus->spi);
rx_buffer++;
size--;
tx_allowed = true;
}
}
furi_hal_spi_bus_end_txrx(bus, timeout);
return ret;
}
void furi_hal_spi_device_configure(const FuriHalSpiDevice* device) {
assert(device);
assert(device->config);
furi_hal_spi_bus_configure(device->bus, device->config);
}
const FuriHalSpiDevice* furi_hal_spi_device_get(FuriHalSpiDeviceId device_id) {
assert(device_id < FuriHalSpiDeviceIdMax);
const FuriHalSpiDevice* device = &furi_hal_spi_devices[device_id];
assert(device);
furi_hal_spi_bus_lock(device->bus);
furi_hal_spi_device_configure(device);
return device;
}
void furi_hal_spi_device_return(const FuriHalSpiDevice* device) {
furi_hal_spi_bus_unlock(device->bus);
}
bool furi_hal_spi_device_rx(
const FuriHalSpiDevice* device,
uint8_t* buffer,
size_t size,
uint32_t timeout) {
assert(device);
assert(buffer);
assert(size > 0);
if(device->chip_select) {
hal_gpio_write(device->chip_select, false);
}
bool ret = furi_hal_spi_bus_rx(device->bus, buffer, size, timeout);
if(device->chip_select) {
hal_gpio_write(device->chip_select, true);
}
return ret;
}
bool furi_hal_spi_device_tx(
const FuriHalSpiDevice* device,
uint8_t* buffer,
size_t size,
uint32_t timeout) {
assert(device);
assert(buffer);
assert(size > 0);
if(device->chip_select) {
hal_gpio_write(device->chip_select, false);
}
bool ret = furi_hal_spi_bus_tx(device->bus, buffer, size, timeout);
if(device->chip_select) {
hal_gpio_write(device->chip_select, true);
}
return ret;
}
bool furi_hal_spi_device_trx(
const FuriHalSpiDevice* device,
uint8_t* tx_buffer,
uint8_t* rx_buffer,
size_t size,
uint32_t timeout) {
assert(device);
assert(tx_buffer);
assert(rx_buffer);
assert(size > 0);
if(device->chip_select) {
hal_gpio_write(device->chip_select, false);
}
bool ret = furi_hal_spi_bus_trx(device->bus, tx_buffer, rx_buffer, size, timeout);
if(device->chip_select) {
hal_gpio_write(device->chip_select, true);
}
return ret;
}

View File

@ -0,0 +1,129 @@
#pragma once
#include "main.h"
#include "furi-hal-spi-config.h"
#include <furi-hal-gpio.h>
#include <stdbool.h>
#include <stddef.h>
#ifdef __cplusplus
extern "C" {
#endif
/**
* Init SPI API
*/
void furi_hal_spi_init();
/* Bus Level API */
/** Lock SPI bus
* Takes bus mutex, if used
*/
void furi_hal_spi_bus_lock(const FuriHalSpiBus* bus);
/** Unlock SPI bus
* Releases BUS mutex, if used
*/
void furi_hal_spi_bus_unlock(const FuriHalSpiBus* bus);
/**
* Configure SPI bus
* @param bus - spi bus handler
* @param config - spi configuration structure
*/
void furi_hal_spi_bus_configure(const FuriHalSpiBus* bus, const LL_SPI_InitTypeDef* config);
/** SPI Receive
* @param bus - spi bus handler
* @param buffer - receive buffer
* @param size - transaction size
* @param timeout - bus operation timeout in ms
*/
bool furi_hal_spi_bus_rx(const FuriHalSpiBus* bus, uint8_t* buffer, size_t size, uint32_t timeout);
/** SPI Transmit
* @param bus - spi bus handler
* @param buffer - transmit buffer
* @param size - transaction size
* @param timeout - bus operation timeout in ms
*/
bool furi_hal_spi_bus_tx(const FuriHalSpiBus* bus, uint8_t* buffer, size_t size, uint32_t timeout);
/** SPI Transmit and Receive
* @param bus - spi bus handlere
* @param tx_buffer - device handle
* @param rx_buffer - device handle
* @param size - transaction size
* @param timeout - bus operation timeout in ms
*/
bool furi_hal_spi_bus_trx(
const FuriHalSpiBus* bus,
uint8_t* tx_buffer,
uint8_t* rx_buffer,
size_t size,
uint32_t timeout);
/* Device Level API */
/** Reconfigure SPI bus for device
* @param device - device description
*/
void furi_hal_spi_device_configure(const FuriHalSpiDevice* device);
/** Get Device handle
* And lock access to the corresponding SPI BUS
* @param device_id - device identifier
* @return device handle
*/
const FuriHalSpiDevice* furi_hal_spi_device_get(FuriHalSpiDeviceId device_id);
/** Return Device handle
* And unlock access to the corresponding SPI BUS
* @param device - device handle
*/
void furi_hal_spi_device_return(const FuriHalSpiDevice* device);
/** SPI Recieve
* @param device - device handle
* @param buffer - receive buffer
* @param size - transaction size
* @param timeout - bus operation timeout in ms
*/
bool furi_hal_spi_device_rx(
const FuriHalSpiDevice* device,
uint8_t* buffer,
size_t size,
uint32_t timeout);
/** SPI Transmit
* @param device - device handle
* @param buffer - transmit buffer
* @param size - transaction size
* @param timeout - bus operation timeout in ms
*/
bool furi_hal_spi_device_tx(
const FuriHalSpiDevice* device,
uint8_t* buffer,
size_t size,
uint32_t timeout);
/** SPI Transmit and Receive
* @param device - device handle
* @param tx_buffer - device handle
* @param rx_buffer - device handle
* @param size - transaction size
* @param timeout - bus operation timeout in ms
*/
bool furi_hal_spi_device_trx(
const FuriHalSpiDevice* device,
uint8_t* tx_buffer,
uint8_t* rx_buffer,
size_t size,
uint32_t timeout);
#ifdef __cplusplus
}
#endif

View File

@ -0,0 +1,7 @@
#include <furi-hal.h>
void furi_hal_init() {
furi_hal_i2c_init();
furi_hal_light_init();
furi_hal_spi_init();
}

View File

@ -0,0 +1,8 @@
#pragma once
#include <furi-hal-i2c.h>
#include <furi-hal-light.h>
#include <furi-hal-resources.h>
#include <furi-hal-spi.h>
void furi_hal_init();

View File

@ -0,0 +1,108 @@
#pragma once
#include <stm32wbxx.h>
#include <stm32wbxx_ll_gpio.h>
#include <stm32wbxx_ll_spi.h>
#define BUTTON_BACK_GPIO_Port GPIOC
#define BUTTON_BACK_Pin LL_GPIO_PIN_13
#define BUTTON_DOWN_GPIO_Port GPIOC
#define BUTTON_DOWN_Pin LL_GPIO_PIN_6
#define BUTTON_LEFT_GPIO_Port GPIOB
#define BUTTON_LEFT_Pin LL_GPIO_PIN_11
#define BUTTON_OK_GPIO_Port GPIOH
#define BUTTON_OK_Pin LL_GPIO_PIN_3
#define BUTTON_RIGHT_GPIO_Port GPIOB
#define BUTTON_RIGHT_Pin LL_GPIO_PIN_12
#define BUTTON_UP_GPIO_Port GPIOB
#define BUTTON_UP_Pin LL_GPIO_PIN_10
#define CC1101_CS_GPIO_Port GPIOD
#define CC1101_CS_Pin LL_GPIO_PIN_0
#define CC1101_G0_GPIO_Port GPIOA
#define CC1101_G0_Pin LL_GPIO_PIN_1
#define DISPLAY_CS_GPIO_Port GPIOC
#define DISPLAY_CS_Pin LL_GPIO_PIN_11
#define DISPLAY_DI_GPIO_Port GPIOB
#define DISPLAY_DI_Pin LL_GPIO_PIN_1
#define DISPLAY_RST_GPIO_Port GPIOB
#define DISPLAY_RST_Pin LL_GPIO_PIN_0
#define IR_RX_GPIO_Port GPIOA
#define IR_RX_Pin LL_GPIO_PIN_0
#define IR_TX_GPIO_Port GPIOB
#define IR_TX_Pin LL_GPIO_PIN_9
#define NFC_CS_GPIO_Port GPIOE
#define NFC_CS_Pin LL_GPIO_PIN_4
#define PA4_GPIO_Port GPIOA
#define PA4_Pin LL_GPIO_PIN_4
#define PA6_GPIO_Port GPIOA
#define PA6_Pin LL_GPIO_PIN_6
#define PA7_GPIO_Port GPIOA
#define PA7_Pin LL_GPIO_PIN_7
#define PB2_GPIO_Port GPIOB
#define PB2_Pin LL_GPIO_PIN_2
#define PB3_GPIO_Port GPIOB
#define PB3_Pin LL_GPIO_PIN_3
#define PC0_GPIO_Port GPIOC
#define PC0_Pin LL_GPIO_PIN_0
#define PC1_GPIO_Port GPIOC
#define PC1_Pin LL_GPIO_PIN_1
#define PC3_GPIO_Port GPIOC
#define PC3_Pin LL_GPIO_PIN_3
#define PERIPH_POWER_GPIO_Port GPIOA
#define PERIPH_POWER_Pin LL_GPIO_PIN_3
#define QUARTZ_32MHZ_IN_GPIO_Port GPIOC
#define QUARTZ_32MHZ_IN_Pin LL_GPIO_PIN_14
#define QUARTZ_32MHZ_OUT_GPIO_Port GPIOC
#define QUARTZ_32MHZ_OUT_Pin LL_GPIO_PIN_15
#define RFID_OUT_GPIO_Port GPIOB
#define RFID_OUT_Pin LL_GPIO_PIN_13
#define RFID_PULL_GPIO_Port GPIOA
#define RFID_PULL_Pin LL_GPIO_PIN_2
#define RFID_RF_IN_GPIO_Port GPIOC
#define RFID_RF_IN_Pin LL_GPIO_PIN_5
#define RFID_TUNE_GPIO_Port GPIOA
#define RFID_TUNE_Pin LL_GPIO_PIN_8
#define RF_SW_0_GPIO_Port GPIOC
#define RF_SW_0_Pin LL_GPIO_PIN_4
#define SD_CD_GPIO_Port GPIOC
#define SD_CD_Pin LL_GPIO_PIN_10
#define SD_CS_GPIO_Port GPIOC
#define SD_CS_Pin LL_GPIO_PIN_12
#define SPEAKER_GPIO_Port GPIOB
#define SPEAKER_Pin LL_GPIO_PIN_8
#define VIBRO_GPIO_Port GPIOA
#define VIBRO_Pin LL_GPIO_PIN_15
#define iBTN_GPIO_Port GPIOB
#define iBTN_Pin LL_GPIO_PIN_14
#define USART1_TX_Pin LL_GPIO_PIN_6
#define USART1_TX_Port GPIOB
#define USART1_RX_Pin LL_GPIO_PIN_7
#define USART1_RX_Port GPIOB
#define SPI_D_MISO_GPIO_Port GPIOC
#define SPI_D_MISO_Pin LL_GPIO_PIN_2
#define SPI_D_MOSI_GPIO_Port GPIOB
#define SPI_D_MOSI_Pin LL_GPIO_PIN_15
#define SPI_D_SCK_GPIO_Port GPIOD
#define SPI_D_SCK_Pin LL_GPIO_PIN_1
#define SPI_R_MISO_GPIO_Port GPIOB
#define SPI_R_MISO_Pin LL_GPIO_PIN_4
#define SPI_R_MOSI_GPIO_Port GPIOB
#define SPI_R_MOSI_Pin LL_GPIO_PIN_5
#define SPI_R_SCK_GPIO_Port GPIOA
#define SPI_R_SCK_Pin LL_GPIO_PIN_5

View File

@ -0,0 +1,69 @@
#include <u8g2.h>
#include <assert.h>
#include <furi-hal.h>
#include <stm32wbxx_ll_utils.h>
static FuriHalSpiDevice* u8g2_periphery_display = NULL;
uint8_t u8g2_gpio_and_delay_stm32(u8x8_t* u8x8, uint8_t msg, uint8_t arg_int, void* arg_ptr) {
switch(msg) {
case U8X8_MSG_GPIO_AND_DELAY_INIT:
/* HAL initialization contains all what we need so we can skip this part. */
break;
case U8X8_MSG_DELAY_MILLI:
LL_mDelay(arg_int);
break;
case U8X8_MSG_DELAY_10MICRO:
LL_mDelay(1);
break;
case U8X8_MSG_DELAY_100NANO:
asm("nop");
break;
case U8X8_MSG_GPIO_RESET:
hal_gpio_write(&gpio_display_rst, arg_int);
break;
default:
return 0;
}
return 1;
}
uint8_t u8x8_hw_spi_stm32(u8x8_t* u8x8, uint8_t msg, uint8_t arg_int, void* arg_ptr) {
switch(msg) {
case U8X8_MSG_BYTE_SEND:
furi_hal_spi_bus_tx(u8g2_periphery_display->bus, (uint8_t*)arg_ptr, arg_int, 10000);
break;
case U8X8_MSG_BYTE_SET_DC:
hal_gpio_write(&gpio_display_di, arg_int);
break;
case U8X8_MSG_BYTE_INIT:
break;
case U8X8_MSG_BYTE_START_TRANSFER:
assert(u8g2_periphery_display == NULL);
u8g2_periphery_display =
(FuriHalSpiDevice*)furi_hal_spi_device_get(FuriHalSpiDeviceIdDisplay);
hal_gpio_write(u8g2_periphery_display->chip_select, false);
break;
case U8X8_MSG_BYTE_END_TRANSFER:
assert(u8g2_periphery_display);
hal_gpio_write(u8g2_periphery_display->chip_select, true);
furi_hal_spi_device_return(u8g2_periphery_display);
u8g2_periphery_display = NULL;
break;
default:
return 0;
}
return 1;
}

View File

@ -0,0 +1,187 @@
/**
*****************************************************************************
**
** File : stm32wb55xx_flash_cm4.ld
**
** Abstract : System Workbench Minimal System calls file
**
** For more information about which c-functions
** need which of these lowlevel functions
** please consult the Newlib libc-manual
**
** Environment : System Workbench for MCU
**
** Distribution: The file is distributed “as is,” without any warranty
** of any kind.
**
*****************************************************************************
**
** <h2><center>&copy; COPYRIGHT(c) 2019 Ac6</center></h2>
**
** Redistribution and use in source and binary forms, with or without modification,
** are permitted provided that the following conditions are met:
** 1. Redistributions of source code must retain the above copyright notice,
** this list of conditions and the following disclaimer.
** 2. Redistributions in binary form must reproduce the above copyright notice,
** this list of conditions and the following disclaimer in the documentation
** and/or other materials provided with the distribution.
** 3. Neither the name of Ac6 nor the names of its contributors
** may be used to endorse or promote products derived from this software
** without specific prior written permission.
**
** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
** AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
** IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
** DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
** FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
** DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
** SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
** CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
** OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
**
*****************************************************************************
*/
/* Entry Point */
ENTRY(Reset_Handler)
/* Highest address of the user mode stack */
_estack = 0x20030000; /* end of RAM */
/* Generate a link error if heap and stack don't fit into RAM */
_Min_Heap_Size = 0x200; /* required amount of heap */
_Min_Stack_Size = 0x400; /* required amount of stack */
/* Specify the memory areas */
MEMORY
{
FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 32K
RAM1 (xrw) : ORIGIN = 0x20000008, LENGTH = 0x2FFF8
RAM_SHARED (xrw) : ORIGIN = 0x20030000, LENGTH = 10K
}
/* Define output sections */
SECTIONS
{
/* The startup code goes first into FLASH */
.isr_vector :
{
. = ALIGN(4);
KEEP(*(.isr_vector)) /* Startup code */
. = ALIGN(4);
} >FLASH
/* The program code and other data goes into FLASH */
.text :
{
. = ALIGN(4);
*(.text) /* .text sections (code) */
*(.text*) /* .text* sections (code) */
*(.glue_7) /* glue arm to thumb code */
*(.glue_7t) /* glue thumb to arm code */
*(.eh_frame)
KEEP (*(.init))
KEEP (*(.fini))
. = ALIGN(4);
_etext = .; /* define a global symbols at end of code */
} >FLASH
/* Constant data goes into FLASH */
.rodata :
{
. = ALIGN(4);
*(.rodata) /* .rodata sections (constants, strings, etc.) */
*(.rodata*) /* .rodata* sections (constants, strings, etc.) */
. = ALIGN(4);
} >FLASH
.ARM.extab : { *(.ARM.extab* .gnu.linkonce.armextab.*) } >FLASH
.ARM : {
__exidx_start = .;
*(.ARM.exidx*)
__exidx_end = .;
} >FLASH
.preinit_array :
{
PROVIDE_HIDDEN (__preinit_array_start = .);
KEEP (*(.preinit_array*))
PROVIDE_HIDDEN (__preinit_array_end = .);
} >FLASH
.init_array :
{
PROVIDE_HIDDEN (__init_array_start = .);
KEEP (*(SORT(.init_array.*)))
KEEP (*(.init_array*))
PROVIDE_HIDDEN (__init_array_end = .);
} >FLASH
.fini_array :
{
PROVIDE_HIDDEN (__fini_array_start = .);
KEEP (*(SORT(.fini_array.*)))
KEEP (*(.fini_array*))
PROVIDE_HIDDEN (__fini_array_end = .);
} >FLASH
/* used by the startup to initialize data */
_sidata = LOADADDR(.data);
/* Initialized data sections goes into RAM, load LMA copy after code */
.data :
{
. = ALIGN(4);
_sdata = .; /* create a global symbol at data start */
*(.data) /* .data sections */
*(.data*) /* .data* sections */
. = ALIGN(4);
_edata = .; /* define a global symbol at data end */
} >RAM1 AT> FLASH
/* Uninitialized data section */
. = ALIGN(4);
.bss :
{
/* This is used by the startup in order to initialize the .bss secion */
_sbss = .; /* define a global symbol at bss start */
__bss_start__ = _sbss;
*(.bss)
*(.bss*)
*(COMMON)
. = ALIGN(4);
_ebss = .; /* define a global symbol at bss end */
__bss_end__ = _ebss;
} >RAM1
/* User_heap_stack section, used to check that there is enough RAM left */
._user_heap_stack :
{
. = ALIGN(8);
PROVIDE ( end = . );
PROVIDE ( _end = . );
. = . + _Min_Heap_Size;
. = . + _Min_Stack_Size;
. = ALIGN(8);
} >RAM1
/* Remove information from the standard libraries */
/DISCARD/ :
{
libc.a ( * )
libm.a ( * )
libgcc.a ( * )
}
.ARM.attributes 0 : { *(.ARM.attributes) }
MAPPING_TABLE (NOLOAD) : { *(MAPPING_TABLE) } >RAM_SHARED
MB_MEM1 (NOLOAD) : { *(MB_MEM1) } >RAM_SHARED
MB_MEM2 (NOLOAD) : { _sMB_MEM2 = . ; *(MB_MEM2) ; _eMB_MEM2 = . ; } >RAM_SHARED
}

View File

@ -0,0 +1,264 @@
#include <target.h>
#include <stm32wbxx.h>
#include <stm32wbxx_ll_system.h>
#include <stm32wbxx_ll_bus.h>
#include <stm32wbxx_ll_utils.h>
#include <stm32wbxx_ll_rcc.h>
#include <stm32wbxx_ll_rtc.h>
#include <stm32wbxx_ll_pwr.h>
#include <stm32wbxx_ll_gpio.h>
#include <stm32wbxx_hal_flash.h>
#include <lib/toolbox/version.h>
#include <furi-hal.h>
#include <u8g2.h>
const uint8_t I_DFU_128x50[] = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x07, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0x00, 0x38, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0E, 0x00, 0xC0, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x8F, 0x01, 0x00, 0x00, 0x03, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x7F, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0xFF, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0xFF, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0xFF, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0x75, 0x00, 0x00, 0xF0, 0x3F, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xFF, 0x0A, 0x00, 0x00, 0x0F, 0x60, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0xE0, 0x0F, 0x00, 0xC0, 0xE0, 0x4F, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x18, 0x00, 0x30, 0x1E, 0x90, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x18, 0x00, 0x8C, 0x01, 0xA0, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x81, 0xFF, 0x19, 0x00, 0x63, 0x00, 0xC0, 0xF0, 0x07,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x60, 0x5E, 0x1F, 0x80, 0x18, 0x00, 0xE0, 0x0E, 0x18,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x18, 0xAF, 0x0F, 0x40, 0x06, 0x00, 0xF8, 0x01, 0x20,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x06, 0x57, 0x01, 0x20, 0x01, 0x00, 0x78, 0x00, 0x3E,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x84, 0x81, 0xAF, 0x02, 0x90, 0x00, 0x00, 0x38, 0x80, 0x41,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x63, 0x80, 0x57, 0x01, 0x48, 0x00, 0x00, 0x10, 0x60, 0x40,
0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0x10, 0x80, 0xAB, 0x00, 0x24, 0x00, 0x00, 0x08, 0x10, 0x40,
0x3F, 0x00, 0x00, 0x00, 0x00, 0x38, 0x0C, 0xC0, 0x57, 0x01, 0x12, 0x00, 0x00, 0x04, 0x08, 0x40,
0xC0, 0x0F, 0x00, 0x00, 0xC0, 0x07, 0x03, 0xF0, 0xAB, 0x00, 0x0A, 0x00, 0x00, 0x02, 0x04, 0x40,
0x00, 0xF0, 0x1F, 0x80, 0x3F, 0xC0, 0x00, 0xFC, 0x55, 0x01, 0x05, 0xE0, 0x00, 0x01, 0x04, 0x40,
0x00, 0x00, 0xE0, 0x7F, 0x00, 0x30, 0x00, 0xFF, 0xAB, 0x00, 0x05, 0xE0, 0x80, 0x00, 0x02, 0x40,
0x0F, 0x00, 0x00, 0x00, 0x80, 0x0F, 0xE0, 0xCF, 0x55, 0x81, 0x02, 0xF0, 0x40, 0x00, 0x02, 0x40,
0xF0, 0x0F, 0x00, 0x00, 0x7F, 0x00, 0xFE, 0xC3, 0xAB, 0x80, 0x02, 0x78, 0x20, 0x00, 0x01, 0x40,
0x00, 0xF0, 0xFF, 0xFF, 0x00, 0xF0, 0xFF, 0xC0, 0xD5, 0x81, 0x01, 0x7E, 0x10, 0x80, 0x00, 0x20,
0x00, 0x00, 0x00, 0x00, 0xC0, 0xFF, 0x0F, 0xE0, 0xFA, 0x83, 0xC1, 0x3F, 0x08, 0x80, 0x00, 0x20,
0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x01, 0xD8, 0x07, 0x83, 0xF1, 0x1F, 0x04, 0x40, 0x00, 0x20,
0x00, 0xE0, 0xFF, 0xFF, 0xFF, 0x0F, 0x80, 0xC7, 0x01, 0x83, 0xF1, 0x0F, 0x00, 0x20, 0x00, 0x10,
0xE0, 0xFF, 0xFF, 0xFF, 0x3F, 0xC0, 0x7F, 0x40, 0x80, 0x83, 0xE1, 0x01, 0x00, 0x20, 0x00, 0x18,
0xFC, 0xFF, 0xFF, 0xFF, 0x03, 0x3F, 0x00, 0x20, 0xFC, 0x83, 0x01, 0x00, 0x00, 0x10, 0x00, 0x18,
0xFF, 0xFF, 0xFF, 0x3F, 0xF0, 0x00, 0x00, 0x10, 0xD7, 0x01, 0x03, 0x00, 0x00, 0x08, 0x00, 0x1C,
0xFF, 0xFF, 0x01, 0x00, 0x0F, 0x00, 0x00, 0x88, 0xAB, 0x02, 0xE3, 0x01, 0x00, 0x08, 0x00, 0x0C,
0xFF, 0x07, 0x00, 0xE0, 0x00, 0x00, 0x00, 0xC4, 0x55, 0x05, 0x1E, 0x00, 0x00, 0x04, 0x00, 0x0E,
0x7F, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x00, 0xA3, 0xAB, 0x02, 0x06, 0x00, 0x00, 0x02, 0x00, 0x0F,
0x0F, 0x00, 0x80, 0x03, 0x00, 0x00, 0xC0, 0x10, 0x57, 0x05, 0x02, 0x00, 0x00, 0x01, 0x80, 0x07,
0x03, 0x00, 0x70, 0x00, 0x00, 0x00, 0x30, 0x08, 0xAB, 0x0A, 0x02, 0x00, 0xC0, 0x00, 0xC0, 0x07,
0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x0C, 0x84, 0x57, 0x15, 0x01, 0x00, 0x30, 0x00, 0xE0, 0x07,
0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0xC3, 0xFF, 0x2A, 0x01, 0x00, 0x0C, 0x00, 0xF0, 0x0F,
0x00, 0xC0, 0x00, 0x00, 0x00, 0xE0, 0xC0, 0xE0, 0xFE, 0x55, 0x01, 0x82, 0x03, 0x00, 0xF8, 0x15,
0x00, 0x30, 0x00, 0x00, 0x00, 0x1C, 0x30, 0x78, 0xFE, 0xAA, 0x01, 0x7C, 0x00, 0x00, 0xFC, 0x23,
0x00, 0x0E, 0x00, 0x00, 0xC0, 0x03, 0x0C, 0x3C, 0x7F, 0x5D, 0x01, 0x00, 0x00, 0x00, 0xFF, 0x45,
0xC0, 0x01, 0x00, 0x00, 0x3E, 0x00, 0x02, 0x8F, 0xBF, 0xAE, 0x03, 0x00, 0x00, 0xC0, 0xFF, 0x82,
0x30, 0x00, 0x00, 0xC0, 0x01, 0x80, 0xC1, 0x43, 0xFE, 0x5D, 0x01, 0x00, 0x00, 0xF0, 0xFF, 0x05,
0x0F, 0x00, 0x80, 0x3F, 0x00, 0x60, 0xF0, 0x31, 0xF6, 0xAE, 0x03, 0x00, 0x00, 0xFA, 0xAF, 0x02,
0xFC, 0xFF, 0x7F, 0x00, 0x00, 0x18, 0x7C, 0x08, 0x23, 0xFF, 0x05, 0x00, 0x00, 0xFD, 0x55, 0x01,
0x00, 0x00, 0x00, 0x00, 0x00, 0x86, 0x1F, 0x84, 0x30, 0xFE, 0x0A, 0x00, 0x00, 0xAA, 0xAA, 0x00,
0x00, 0x00, 0x00, 0x00, 0x80, 0xF1, 0x07, 0x43, 0x18, 0xFF, 0x15, 0x00, 0x00, 0x54, 0x15, 0x00,
0x00, 0x00, 0x00, 0x00, 0xF8, 0xFF, 0x80, 0x20, 0x8C, 0xFF, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00,
};
// Boot request enum
#define BOOT_REQUEST_TAINTED 0x00000000
#define BOOT_REQUEST_CLEAN 0xDADEDADE
#define BOOT_REQUEST_DFU 0xDF00B000
// Boot to DFU pin
#define BOOT_DFU_PORT GPIOB
#define BOOT_DFU_PIN LL_GPIO_PIN_11
// USB pins
#define BOOT_USB_PORT GPIOA
#define BOOT_USB_DM_PIN LL_GPIO_PIN_11
#define BOOT_USB_DP_PIN LL_GPIO_PIN_12
#define BOOT_USB_PIN (BOOT_USB_DM_PIN | BOOT_USB_DP_PIN)
#define RTC_CLOCK_IS_READY() (LL_RCC_LSE_IsReady() && LL_RCC_LSI1_IsReady())
uint8_t u8g2_gpio_and_delay_stm32(u8x8_t* u8x8, uint8_t msg, uint8_t arg_int, void* arg_ptr);
uint8_t u8x8_hw_spi_stm32(u8x8_t* u8x8, uint8_t msg, uint8_t arg_int, void* arg_ptr);
void target_led_control(char* c) {
furi_hal_light_set(LightRed, 0x00);
furi_hal_light_set(LightGreen, 0x00);
furi_hal_light_set(LightBlue, 0x00);
do {
if(*c == 'R') {
furi_hal_light_set(LightRed, 0xFF);
} else if(*c == 'G') {
furi_hal_light_set(LightGreen, 0xFF);
} else if(*c == 'B') {
furi_hal_light_set(LightBlue, 0xFF);
} else if(*c == '.') {
LL_mDelay(125);
furi_hal_light_set(LightRed, 0x00);
furi_hal_light_set(LightGreen, 0x00);
furi_hal_light_set(LightBlue, 0x00);
LL_mDelay(125);
} else if(*c == '-') {
LL_mDelay(250);
furi_hal_light_set(LightRed, 0x00);
furi_hal_light_set(LightGreen, 0x00);
furi_hal_light_set(LightBlue, 0x00);
LL_mDelay(250);
} else if(*c == '|') {
furi_hal_light_set(LightRed, 0x00);
furi_hal_light_set(LightGreen, 0x00);
furi_hal_light_set(LightBlue, 0x00);
}
c++;
} while(*c != 0);
}
void target_clock_init() {
LL_Init1msTick(4000000);
LL_SetSystemCoreClock(4000000);
LL_AHB2_GRP1_EnableClock(LL_AHB2_GRP1_PERIPH_GPIOA);
LL_AHB2_GRP1_EnableClock(LL_AHB2_GRP1_PERIPH_GPIOB);
LL_AHB2_GRP1_EnableClock(LL_AHB2_GRP1_PERIPH_GPIOC);
LL_AHB2_GRP1_EnableClock(LL_AHB2_GRP1_PERIPH_GPIOD);
LL_AHB2_GRP1_EnableClock(LL_AHB2_GRP1_PERIPH_GPIOE);
LL_AHB2_GRP1_EnableClock(LL_AHB2_GRP1_PERIPH_GPIOH);
LL_APB2_GRP1_EnableClock(LL_APB2_GRP1_PERIPH_SPI1);
LL_APB1_GRP1_EnableClock(LL_APB1_GRP1_PERIPH_SPI2);
}
void target_gpio_init() {
// USB D+
LL_GPIO_SetPinMode(BOOT_USB_PORT, BOOT_USB_DP_PIN, LL_GPIO_MODE_OUTPUT);
LL_GPIO_SetPinSpeed(BOOT_USB_PORT, BOOT_USB_DP_PIN, LL_GPIO_SPEED_FREQ_VERY_HIGH);
LL_GPIO_SetPinOutputType(BOOT_USB_PORT, BOOT_USB_DP_PIN, LL_GPIO_OUTPUT_OPENDRAIN);
// USB D-
LL_GPIO_SetPinMode(BOOT_USB_PORT, BOOT_USB_DM_PIN, LL_GPIO_MODE_OUTPUT);
LL_GPIO_SetPinSpeed(BOOT_USB_PORT, BOOT_USB_DM_PIN, LL_GPIO_SPEED_FREQ_VERY_HIGH);
LL_GPIO_SetPinOutputType(BOOT_USB_PORT, BOOT_USB_DM_PIN, LL_GPIO_OUTPUT_OPENDRAIN);
// Button: back
LL_GPIO_SetPinMode(BOOT_DFU_PORT, BOOT_DFU_PIN, LL_GPIO_MODE_INPUT);
LL_GPIO_SetPinPull(BOOT_DFU_PORT, BOOT_DFU_PIN, LL_GPIO_PULL_UP);
}
void target_rtc_init() {
// LSE and RTC
LL_PWR_EnableBkUpAccess();
if(!RTC_CLOCK_IS_READY()) {
// Start LSI1 needed for CSS
LL_RCC_LSI1_Enable();
// Try to start LSE normal way
LL_RCC_LSE_SetDriveCapability(LL_RCC_LSEDRIVE_HIGH);
LL_RCC_LSE_Enable();
uint32_t c = 0;
while(!RTC_CLOCK_IS_READY() && c < 200) {
LL_mDelay(10);
c++;
}
// Plan B: reset backup domain
if(!RTC_CLOCK_IS_READY()) {
target_led_control("-R.R.R.");
LL_RCC_ForceBackupDomainReset();
LL_RCC_ReleaseBackupDomainReset();
NVIC_SystemReset();
}
// Set RTC domain clock to LSE
LL_RCC_SetRTCClockSource(LL_RCC_RTC_CLKSOURCE_LSE);
// Enable LSE CSS
LL_RCC_LSE_EnableCSS();
}
// Enable clocking
LL_RCC_EnableRTC();
LL_APB1_GRP1_EnableClock(LL_APB1_GRP1_PERIPH_RTCAPB);
}
void target_version_save(void) {
LL_RTC_BAK_SetRegister(RTC, LL_RTC_BKP_DR1, (uint32_t)version_get());
}
void target_usb_wire_reset() {
LL_GPIO_ResetOutputPin(BOOT_USB_PORT, BOOT_USB_PIN);
}
void target_display_init() {
// Prepare gpio
hal_gpio_init_simple(&gpio_display_rst, GpioModeOutputPushPull);
hal_gpio_init_simple(&gpio_display_di, GpioModeOutputPushPull);
// Initialize
u8g2_t fb;
u8g2_Setup_st7565_erc12864_alt_f(&fb, U8G2_R0, u8x8_hw_spi_stm32, u8g2_gpio_and_delay_stm32);
u8g2_InitDisplay(&fb);
u8g2_SetContrast(&fb, 36);
// Create payload
u8g2_ClearBuffer(&fb);
u8g2_SetDrawColor(&fb, 0x01);
u8g2_SetFont(&fb, u8g2_font_helvB08_tf);
u8g2_DrawXBM(&fb, 0, 64 - 50, 128, 50, I_DFU_128x50);
u8g2_DrawStr(&fb, 2, 8, "Update & Recovery Mode");
u8g2_DrawStr(&fb, 2, 21, "DFU started");
// Send buffer
u8g2_SetPowerSave(&fb, 0);
u8g2_SendBuffer(&fb);
}
void target_init() {
target_clock_init();
target_gpio_init();
furi_hal_init();
target_led_control("RGB");
target_rtc_init();
target_version_save();
target_usb_wire_reset();
// Errata 2.2.9, Flash OPTVERR flag is always set after system reset
__HAL_FLASH_CLEAR_FLAG(FLASH_FLAG_ALL_ERRORS);
}
int target_is_dfu_requested() {
if(LL_RTC_BAK_GetRegister(RTC, LL_RTC_BKP_DR0) == BOOT_REQUEST_TAINTED) {
// Default system state is tainted
// We must ensure that MCU is cleanly booted
LL_RTC_BAK_SetRegister(RTC, LL_RTC_BKP_DR0, BOOT_REQUEST_CLEAN);
NVIC_SystemReset();
} else if(LL_RTC_BAK_GetRegister(RTC, LL_RTC_BKP_DR0) == BOOT_REQUEST_DFU) {
return 1;
}
LL_mDelay(100);
if(!LL_GPIO_IsInputPinSet(BOOT_DFU_PORT, BOOT_DFU_PIN)) {
return 1;
}
return 0;
}
void target_switch(void* offset) {
asm volatile("ldr r3, [%0] \n"
"msr msp, r3 \n"
"ldr r3, [%1] \n"
"mov pc, r3 \n"
:
: "r"(offset), "r"(offset + 0x4)
: "r3");
}
void target_switch2dfu() {
target_led_control("B");
furi_hal_light_set(LightBacklight, 0xFF);
target_display_init();
// Mark system as tainted, it will be soon
LL_RTC_BAK_SetRegister(RTC, LL_RTC_BKP_DR0, BOOT_REQUEST_TAINTED);
// Remap memory to system bootloader
LL_SYSCFG_SetRemapMemory(LL_SYSCFG_REMAP_SYSTEMFLASH);
// Jump
target_switch(0x0);
}
void target_switch2os() {
target_led_control("G");
SCB->VTOR = OS_OFFSET;
target_switch((void*)(BOOT_ADDRESS + OS_OFFSET));
}

View File

@ -0,0 +1,48 @@
TOOLCHAIN = arm
BOOT_ADDRESS = 0x08000000
FW_ADDRESS = 0x08008000
OS_OFFSET = 0x00008000
FLASH_ADDRESS = 0x08000000
OPENOCD_OPTS = -f interface/stlink.cfg -c "transport select hla_swd" -f ../debug/stm32wbx.cfg -c "init"
BOOT_CFLAGS = -DBOOT_ADDRESS=$(BOOT_ADDRESS) -DFW_ADDRESS=$(FW_ADDRESS) -DOS_OFFSET=$(OS_OFFSET)
MCU_FLAGS = -mcpu=cortex-m4 -mthumb -mfpu=fpv4-sp-d16 -mfloat-abi=hard
CFLAGS += $(MCU_FLAGS) $(BOOT_CFLAGS) -DSTM32WB55xx -Wall -fdata-sections -ffunction-sections
LDFLAGS += $(MCU_FLAGS) -specs=nosys.specs -specs=nano.specs
CUBE_DIR = $(PROJECT_ROOT)/lib/STM32CubeWB
# ST HAL
CFLAGS += -DUSE_FULL_LL_DRIVER
ASM_SOURCES += $(CUBE_DIR)/Drivers/CMSIS/Device/ST/STM32WBxx/Source/Templates/gcc/startup_stm32wb55xx_cm4.s
C_SOURCES += $(CUBE_DIR)/Drivers/CMSIS/Device/ST/STM32WBxx/Source/Templates/system_stm32wbxx.c
C_SOURCES += $(CUBE_DIR)/Drivers/STM32WBxx_HAL_Driver/Src/stm32wbxx_ll_utils.c
C_SOURCES += $(CUBE_DIR)/Drivers/STM32WBxx_HAL_Driver/Src/stm32wbxx_ll_gpio.c
C_SOURCES += $(CUBE_DIR)/Drivers/STM32WBxx_HAL_Driver/Src/stm32wbxx_ll_i2c.c
C_SOURCES += $(CUBE_DIR)/Drivers/STM32WBxx_HAL_Driver/Src/stm32wbxx_ll_spi.c
CFLAGS += -I$(CUBE_DIR)/Drivers/CMSIS/Include
CFLAGS += -I$(CUBE_DIR)/Drivers/CMSIS/Device/ST/STM32WBxx/Include
CFLAGS += -I$(CUBE_DIR)/Drivers/STM32WBxx_HAL_Driver/Inc
LDFLAGS += -T$(TARGET_DIR)/stm32wb55xx_flash_cm4.ld
# Drivers
DRIVERS_DIR = $(PROJECT_ROOT)//lib/drivers
CFLAGS += -I$(DRIVERS_DIR)
C_SOURCES += $(DRIVERS_DIR)/lp5562.c
# API-HAL
CFLAGS += -I$(TARGET_DIR)/furi-hal
C_SOURCES += $(wildcard $(TARGET_DIR)/furi-hal/*.c)
# Version generation
C_SOURCES += $(PROJECT_ROOT)/lib/toolbox/version.c
ASM_SOURCES += $(wildcard $(TARGET_DIR)/*.s)
C_SOURCES += $(wildcard $(TARGET_DIR)/*.c)
CPP_SOURCES += $(wildcard $(TARGET_DIR)/*.cpp)
SVD_FILE = $(PROJECT_ROOT)/debug/STM32WB55_CM4.svd

View File

@ -19,6 +19,15 @@
})
#endif
#ifndef ROUND_UP_TO
#define ROUND_UP_TO(a, b) \
({ \
__typeof__(a) _a = (a); \
__typeof__(b) _b = (b); \
_a / _b + !!(_a % _b); \
})
#endif
#ifndef CLAMP
#define CLAMP(x, upper, lower) (MIN(upper, MAX(x, lower)))
#endif
@ -40,3 +49,11 @@
y = SWAP; \
} while(0)
#endif
#ifndef PLACE_IN_SECTION
#define PLACE_IN_SECTION(x) __attribute__((section(x)))
#endif
#ifndef ALIGN
#define ALIGN(n) __attribute__((aligned(n)))
#endif

View File

@ -6,7 +6,7 @@ CLANG_FORMAT_BIN="/usr/bin/clang-format-12"
PROJECT_DIR=$(pwd)
cd $PROJECT_DIR
cd "$PROJECT_DIR" || exit
echo "RUN C\C++ SYNTAX CHECK"
C_FILES=$(find . \
@ -34,7 +34,7 @@ fi
read -p "Do you want fix syntax? (y/n): " confirm && [[ $confirm == [yY] || $confirm == [yY][eE][sS] ]] || exit 1
cd $PROJECT_DIR
cd "$PROJECT_DIR" || exit
# We use root in container and clang-format rewriting files. We'll need change owner to original
local_user=$(stat -c '%u' .clang-format)

View File

@ -118,6 +118,14 @@ to exclude the API function. */
#define INCLUDE_xTaskGetSchedulerState 1
#define INCLUDE_xTimerPendFunctionCall 1
/* CMSIS-RTOS V2 flags */
#define configUSE_OS2_THREAD_SUSPEND_RESUME 1
#define configUSE_OS2_THREAD_ENUMERATE 1
#define configUSE_OS2_EVENTFLAGS_FROM_ISR 1
#define configUSE_OS2_THREAD_FLAGS 1
#define configUSE_OS2_TIMER 1
#define configUSE_OS2_MUTEX 1
/*
* The CMSIS-RTOS V2 FreeRTOS wrapper is dependent on the heap implementation used
* by the application thus the correct define need to be enabled below

View File

@ -6,105 +6,20 @@
#include "ble.h"
#include "tl.h"
#include "app_ble.h"
#include "cmsis_os.h"
#include "shci.h"
#include "otp.h"
#include "dis_app.h"
#include "hrs_app.h"
#include "cmsis_os.h"
#include <furi-hal.h>
typedef struct _tSecurityParams {
uint8_t ioCapability;
uint8_t mitm_mode;
uint8_t bonding_mode;
uint8_t Use_Fixed_Pin;
uint8_t encryptionKeySizeMin;
uint8_t encryptionKeySizeMax;
uint32_t Fixed_Pin;
uint8_t initiateSecurity;
} tSecurityParams;
typedef struct _tBLEProfileGlobalContext {
tSecurityParams bleSecurityParam;
uint16_t gapServiceHandle;
uint16_t devNameCharHandle;
uint16_t appearanceCharHandle;
uint16_t connectionHandle;
uint8_t advtServUUIDlen;
uint8_t advtServUUID[100];
} BleGlobalContext_t;
typedef struct {
BleGlobalContext_t BleApplicationContext_legacy;
APP_BLE_ConnStatus_t Device_Connection_Status;
uint8_t Advertising_mgr_timer_Id;
} BleApplicationContext_t;
#define FAST_ADV_TIMEOUT (30*1000*1000/CFG_TS_TICK_VAL) /**< 30s */
#define INITIAL_ADV_TIMEOUT (60*1000*1000/CFG_TS_TICK_VAL) /**< 60s */
#define BD_ADDR_SIZE_LOCAL 6
#define LED_ON_TIMEOUT (0.005*1000*1000/CFG_TS_TICK_VAL) /**< 5ms */
PLACE_IN_SECTION("MB_MEM1") ALIGN(4) static TL_CmdPacket_t BleCmdBuffer;
static const uint8_t M_bd_addr[BD_ADDR_SIZE_LOCAL] =
{
(uint8_t)((CFG_ADV_BD_ADDRESS & 0x0000000000FF)),
(uint8_t)((CFG_ADV_BD_ADDRESS & 0x00000000FF00) >> 8),
(uint8_t)((CFG_ADV_BD_ADDRESS & 0x000000FF0000) >> 16),
(uint8_t)((CFG_ADV_BD_ADDRESS & 0x0000FF000000) >> 24),
(uint8_t)((CFG_ADV_BD_ADDRESS & 0x00FF00000000) >> 32),
(uint8_t)((CFG_ADV_BD_ADDRESS & 0xFF0000000000) >> 40)
};
static uint8_t bd_addr_udn[BD_ADDR_SIZE_LOCAL];
static const uint8_t BLE_CFG_IR_VALUE[16] = CFG_BLE_IRK;
static const uint8_t BLE_CFG_ER_VALUE[16] = CFG_BLE_ERK;
PLACE_IN_SECTION("TAG_OTA_END") const uint32_t MagicKeywordValue = 0x94448A29 ;
PLACE_IN_SECTION("TAG_OTA_START") const uint32_t MagicKeywordAddress = (uint32_t)&MagicKeywordValue;
PLACE_IN_SECTION("BLE_APP_CONTEXT") static BleApplicationContext_t BleApplicationContext;
PLACE_IN_SECTION("BLE_APP_CONTEXT") static uint16_t AdvIntervalMin, AdvIntervalMax;
uint8_t manuf_data[14] = {
sizeof(manuf_data)-1, AD_TYPE_MANUFACTURER_SPECIFIC_DATA,
0x01/*SKD version */,
0x00 /* Generic*/,
0x00 /* GROUP A Feature */,
0x00 /* GROUP A Feature */,
0x00 /* GROUP B Feature */,
0x00 /* GROUP B Feature */,
0x00, /* BLE MAC start -MSB */
0x00,
0x00,
0x00,
0x00,
0x00, /* BLE MAC stop */
};
// PLACE_IN_SECTION("TAG_OTA_END") const uint32_t MagicKeywordValue = 0x94448A29 ;
// PLACE_IN_SECTION("TAG_OTA_START") const uint32_t MagicKeywordAddress = (uint32_t)&MagicKeywordValue;
osMutexId_t MtxHciId;
osSemaphoreId_t SemHciId;
osThreadId_t AdvUpdateProcessId;
osThreadId_t HciUserEvtProcessId;
const osThreadAttr_t AdvUpdateProcess_attr = {
.name = CFG_ADV_UPDATE_PROCESS_NAME,
.attr_bits = CFG_ADV_UPDATE_PROCESS_ATTR_BITS,
.cb_mem = CFG_ADV_UPDATE_PROCESS_CB_MEM,
.cb_size = CFG_ADV_UPDATE_PROCESS_CB_SIZE,
.stack_mem = CFG_ADV_UPDATE_PROCESS_STACK_MEM,
.priority = CFG_ADV_UPDATE_PROCESS_PRIORITY,
.stack_size = CFG_ADV_UPDATE_PROCESS_STACK_SIZE
};
const osThreadAttr_t HciUserEvtProcess_attr = {
.name = CFG_HCI_USER_EVT_PROCESS_NAME,
.attr_bits = CFG_HCI_USER_EVT_PROCESS_ATTR_BITS,
@ -120,14 +35,6 @@ static void HciUserEvtProcess(void *argument);
static void BLE_UserEvtRx( void * pPayload );
static void BLE_StatusNot( HCI_TL_CmdStatus_t status );
static void Ble_Tl_Init( void );
static void Ble_Hci_Gap_Gatt_Init();
static const uint8_t* BleGetBdAddress( void );
static void Adv_Request( APP_BLE_ConnStatus_t New_Status );
static void Add_Advertisment_Service_UUID( uint16_t servUUID );
static void Adv_Mgr( void );
static void AdvUpdateProcess(void *argument);
static void Adv_Update( void );
bool APP_BLE_Init() {
SHCI_C2_Ble_Init_Cmd_Packet_t ble_init_cmd_packet = {
@ -160,278 +67,6 @@ bool APP_BLE_Init() {
return (SHCI_C2_BLE_Init( &ble_init_cmd_packet ) == SHCI_Success);
}
bool APP_BLE_Start() {
if (APPE_Status() != BleGlueStatusStarted) {
return false;
}
// Initialization of HCI & GATT & GAP layer
Ble_Hci_Gap_Gatt_Init();
// Initialization of the BLE Services
SVCCTL_Init();
// Initialization of the BLE App Context
BleApplicationContext.Device_Connection_Status = APP_BLE_IDLE;
BleApplicationContext.BleApplicationContext_legacy.connectionHandle = 0xFFFF;
// From here, all initialization are BLE application specific
AdvUpdateProcessId = osThreadNew(AdvUpdateProcess, NULL, &AdvUpdateProcess_attr);
// Initialization of ADV - Ad Manufacturer Element - Support OTA Bit Masks
#if(BLE_CFG_OTA_REBOOT_CHAR != 0)
manuf_data[sizeof(manuf_data)-8] = CFG_FEATURE_OTA_REBOOT;
#endif
// Initialize DIS Application
DISAPP_Init();
// Initialize HRS Application
HRSAPP_Init();
// Create timer to handle the connection state machine
HW_TS_Create(CFG_TIM_PROC_ID_ISR, &(BleApplicationContext.Advertising_mgr_timer_Id), hw_ts_SingleShot, Adv_Mgr);
// Make device discoverable
BleApplicationContext.BleApplicationContext_legacy.advtServUUID[0] = AD_TYPE_16_BIT_SERV_UUID;
BleApplicationContext.BleApplicationContext_legacy.advtServUUIDlen = 1;
Add_Advertisment_Service_UUID(HEART_RATE_SERVICE_UUID);
/* Initialize intervals for reconnexion without intervals update */
AdvIntervalMin = CFG_FAST_CONN_ADV_INTERVAL_MIN;
AdvIntervalMax = CFG_FAST_CONN_ADV_INTERVAL_MAX;
Adv_Request(APP_BLE_FAST_ADV);
return true;
}
SVCCTL_UserEvtFlowStatus_t SVCCTL_App_Notification( void *pckt )
{
hci_event_pckt *event_pckt;
evt_le_meta_event *meta_evt;
evt_blue_aci *blue_evt;
hci_le_phy_update_complete_event_rp0 *evt_le_phy_update_complete;
uint8_t TX_PHY, RX_PHY;
tBleStatus ret = BLE_STATUS_INVALID_PARAMS;
event_pckt = (hci_event_pckt*) ((hci_uart_pckt *) pckt)->data;
switch (event_pckt->evt) {
case EVT_DISCONN_COMPLETE:
{
hci_disconnection_complete_event_rp0 *disconnection_complete_event;
disconnection_complete_event = (hci_disconnection_complete_event_rp0 *) event_pckt->data;
if (disconnection_complete_event->Connection_Handle == BleApplicationContext.BleApplicationContext_legacy.connectionHandle) {
BleApplicationContext.BleApplicationContext_legacy.connectionHandle = 0;
BleApplicationContext.Device_Connection_Status = APP_BLE_IDLE;
APP_DBG_MSG("\r\n\r** DISCONNECTION EVENT WITH CLIENT \r\n");
}
/* restart advertising */
Adv_Request(APP_BLE_FAST_ADV);
furi_hal_power_insomnia_exit();
}
break; /* EVT_DISCONN_COMPLETE */
case EVT_LE_META_EVENT:
{
meta_evt = (evt_le_meta_event*) event_pckt->data;
switch (meta_evt->subevent)
{
case EVT_LE_CONN_UPDATE_COMPLETE:
APP_DBG_MSG("\r\n\r** CONNECTION UPDATE EVENT WITH CLIENT \r\n");
/* USER CODE BEGIN EVT_LE_CONN_UPDATE_COMPLETE */
/* USER CODE END EVT_LE_CONN_UPDATE_COMPLETE */
break;
case EVT_LE_PHY_UPDATE_COMPLETE:
APP_DBG_MSG("EVT_UPDATE_PHY_COMPLETE \r\n");
evt_le_phy_update_complete = (hci_le_phy_update_complete_event_rp0*)meta_evt->data;
if (evt_le_phy_update_complete->Status == 0)
{
APP_DBG_MSG("EVT_UPDATE_PHY_COMPLETE, status ok \r\n");
}
else
{
APP_DBG_MSG("EVT_UPDATE_PHY_COMPLETE, status nok \r\n");
}
ret = hci_le_read_phy(BleApplicationContext.BleApplicationContext_legacy.connectionHandle,&TX_PHY,&RX_PHY);
if (ret == BLE_STATUS_SUCCESS)
{
APP_DBG_MSG("Read_PHY success \r\n");
if ((TX_PHY == TX_2M) && (RX_PHY == RX_2M))
{
APP_DBG_MSG("PHY Param TX= %d, RX= %d \r\n", TX_PHY, RX_PHY);
}
else
{
APP_DBG_MSG("PHY Param TX= %d, RX= %d \r\n", TX_PHY, RX_PHY);
}
}
else
{
APP_DBG_MSG("Read conf not succeess \r\n");
}
break;
case EVT_LE_CONN_COMPLETE:
{
furi_hal_power_insomnia_enter();
hci_le_connection_complete_event_rp0 *connection_complete_event;
/**
* The connection is done, there is no need anymore to schedule the LP ADV
*/
connection_complete_event = (hci_le_connection_complete_event_rp0 *) meta_evt->data;
HW_TS_Stop(BleApplicationContext.Advertising_mgr_timer_Id);
APP_DBG_MSG("EVT_LE_CONN_COMPLETE for connection handle 0x%x\r\n", connection_complete_event->Connection_Handle);
if (BleApplicationContext.Device_Connection_Status == APP_BLE_LP_CONNECTING)
{
/* Connection as client */
BleApplicationContext.Device_Connection_Status = APP_BLE_CONNECTED_CLIENT;
}
else
{
/* Connection as server */
BleApplicationContext.Device_Connection_Status = APP_BLE_CONNECTED_SERVER;
}
BleApplicationContext.BleApplicationContext_legacy.connectionHandle = connection_complete_event->Connection_Handle;
}
break; /* HCI_EVT_LE_CONN_COMPLETE */
default:
break;
}
}
break; /* HCI_EVT_LE_META_EVENT */
case EVT_VENDOR:
blue_evt = (evt_blue_aci*) event_pckt->data;
switch (blue_evt->ecode) {
aci_gap_pairing_complete_event_rp0 *pairing_complete;
case EVT_BLUE_GAP_LIMITED_DISCOVERABLE:
APP_DBG_MSG("\r\n\r** EVT_BLUE_GAP_LIMITED_DISCOVERABLE \r\n");
break; /* EVT_BLUE_GAP_LIMITED_DISCOVERABLE */
case EVT_BLUE_GAP_PASS_KEY_REQUEST:
APP_DBG_MSG("\r\n\r** EVT_BLUE_GAP_PASS_KEY_REQUEST \r\n");
aci_gap_pass_key_resp(BleApplicationContext.BleApplicationContext_legacy.connectionHandle,123456);
APP_DBG_MSG("\r\n\r** aci_gap_pass_key_resp \r\n");
break; /* EVT_BLUE_GAP_PASS_KEY_REQUEST */
case EVT_BLUE_GAP_AUTHORIZATION_REQUEST:
APP_DBG_MSG("\r\n\r** EVT_BLUE_GAP_AUTHORIZATION_REQUEST \r\n");
break; /* EVT_BLUE_GAP_AUTHORIZATION_REQUEST */
case EVT_BLUE_GAP_SLAVE_SECURITY_INITIATED:
APP_DBG_MSG("\r\n\r** EVT_BLUE_GAP_SLAVE_SECURITY_INITIATED \r\n");
break; /* EVT_BLUE_GAP_SLAVE_SECURITY_INITIATED */
case EVT_BLUE_GAP_BOND_LOST:
APP_DBG_MSG("\r\n\r** EVT_BLUE_GAP_BOND_LOST \r\n");
aci_gap_allow_rebond(BleApplicationContext.BleApplicationContext_legacy.connectionHandle);
APP_DBG_MSG("\r\n\r** Send allow rebond \r\n");
break; /* EVT_BLUE_GAP_BOND_LOST */
case EVT_BLUE_GAP_DEVICE_FOUND:
APP_DBG_MSG("\r\n\r** EVT_BLUE_GAP_DEVICE_FOUND \r\n");
break; /* EVT_BLUE_GAP_DEVICE_FOUND */
case EVT_BLUE_GAP_ADDR_NOT_RESOLVED:
APP_DBG_MSG("\r\n\r** EVT_BLUE_GAP_DEVICE_FOUND \r\n");
break; /* EVT_BLUE_GAP_DEVICE_FOUND */
case (EVT_BLUE_GAP_KEYPRESS_NOTIFICATION):
APP_DBG_MSG("\r\n\r** EVT_BLUE_GAP_KEYPRESS_NOTIFICATION \r\n");
break; /* EVT_BLUE_GAP_KEY_PRESS_NOTIFICATION */
case (EVT_BLUE_GAP_NUMERIC_COMPARISON_VALUE):
APP_DBG_MSG("numeric_value = %ld\r\n",
((aci_gap_numeric_comparison_value_event_rp0 *)(blue_evt->data))->Numeric_Value);
APP_DBG_MSG("Hex_value = %lx\r\n",
((aci_gap_numeric_comparison_value_event_rp0 *)(blue_evt->data))->Numeric_Value);
aci_gap_numeric_comparison_value_confirm_yesno(BleApplicationContext.BleApplicationContext_legacy.connectionHandle, 1); /* CONFIRM_YES = 1 */
APP_DBG_MSG("\r\n\r** aci_gap_numeric_comparison_value_confirm_yesno-->YES \r\n");
break;
case (EVT_BLUE_GAP_PAIRING_CMPLT):
{
pairing_complete = (aci_gap_pairing_complete_event_rp0*)blue_evt->data;
APP_DBG_MSG("BLE_CTRL_App_Notification: EVT_BLUE_GAP_PAIRING_CMPLT, pairing_complete->Status = %d\r\n",pairing_complete->Status);
if (pairing_complete->Status == 0) {
APP_DBG_MSG("\r\n\r** Pairing OK \r\n");
} else {
APP_DBG_MSG("\r\n\r** Pairing KO \r\n");
}
}
break;
/* USER CODE END ecode */
case EVT_BLUE_GAP_PROCEDURE_COMPLETE:
APP_DBG_MSG("\r\n\r** EVT_BLUE_GAP_PROCEDURE_COMPLETE \r\n");
break;
}
break; /* EVT_VENDOR */
default:
break;
}
return (SVCCTL_UserEvtFlowEnable);
}
APP_BLE_ConnStatus_t APP_BLE_Get_Server_Connection_Status() {
return BleApplicationContext.Device_Connection_Status;
}
/* USER CODE BEGIN FD*/
void APP_BLE_Key_Button1_Action() {
tBleStatus ret = BLE_STATUS_INVALID_PARAMS;
ret = aci_gap_clear_security_db();
if (ret == BLE_STATUS_SUCCESS) {
APP_DBG_MSG("Successfully aci_gap_clear_security_db()\r\n");
} else {
APP_DBG_MSG("aci_gap_clear_security_db() Failed , result: %d \r\n", ret);
}
}
void APP_BLE_Key_Button2_Action() {
tBleStatus ret = BLE_STATUS_INVALID_PARAMS;
ret = aci_gap_slave_security_req(BleApplicationContext.BleApplicationContext_legacy.connectionHandle);
if (ret == BLE_STATUS_SUCCESS) {
APP_DBG_MSG("Successfully aci_gap_slave_security_req()");
} else {
APP_DBG_MSG("aci_gap_slave_security_req() Failed , result: %d \r\n", ret);
}
}
void APP_BLE_Key_Button3_Action() {
uint8_t TX_PHY, RX_PHY;
tBleStatus ret = BLE_STATUS_INVALID_PARAMS;
ret = hci_le_read_phy(BleApplicationContext.BleApplicationContext_legacy.connectionHandle,&TX_PHY,&RX_PHY);
if (ret == BLE_STATUS_SUCCESS) {
APP_DBG_MSG("Read_PHY success \r\n");
APP_DBG_MSG("PHY Param TX= %d, RX= %d \r\n", TX_PHY, RX_PHY);
if ((TX_PHY == TX_2M) && (RX_PHY == RX_2M)) {
APP_DBG_MSG("hci_le_set_phy PHY Param TX= %d, RX= %d \r\n", TX_1M, RX_1M);
ret = hci_le_set_phy(BleApplicationContext.BleApplicationContext_legacy.connectionHandle,ALL_PHYS_PREFERENCE,TX_1M,RX_1M,0);
} else {
APP_DBG_MSG("hci_le_set_phy PHY Param TX= %d, RX= %d \r\n", TX_2M_PREFERRED, RX_2M_PREFERRED);
ret = hci_le_set_phy(BleApplicationContext.BleApplicationContext_legacy.connectionHandle,ALL_PHYS_PREFERENCE,TX_2M_PREFERRED,RX_2M_PREFERRED,0);
}
} else {
APP_DBG_MSG("Read conf not succeess \r\n");
}
if (ret == BLE_STATUS_SUCCESS) {
APP_DBG_MSG("set PHY cmd ok\r\n");
} else {
APP_DBG_MSG("set PHY cmd NOK\r\n");
}
}
static void Ble_Tl_Init( void ) {
HCI_TL_HciInitConf_t Hci_Tl_Init_Conf;
@ -443,308 +78,6 @@ static void Ble_Tl_Init( void ) {
hci_init(BLE_UserEvtRx, (void*) &Hci_Tl_Init_Conf);
}
static void Ble_Hci_Gap_Gatt_Init() {
uint8_t role;
uint16_t gap_service_handle, gap_dev_name_char_handle, gap_appearance_char_handle;
const uint8_t *bd_addr;
uint32_t srd_bd_addr[2];
uint16_t appearance[1] = { BLE_CFG_GAP_APPEARANCE };
/*HCI Reset to synchronise BLE Stack*/
hci_reset();
/**
* Write the BD Address
*/
bd_addr = BleGetBdAddress();
aci_hal_write_config_data(CONFIG_DATA_PUBADDR_OFFSET,
CONFIG_DATA_PUBADDR_LEN,
(uint8_t*) bd_addr);
/* BLE MAC in ADV Packet */
manuf_data[ sizeof(manuf_data)-6] = bd_addr[5];
manuf_data[ sizeof(manuf_data)-5] = bd_addr[4];
manuf_data[ sizeof(manuf_data)-4] = bd_addr[3];
manuf_data[ sizeof(manuf_data)-3] = bd_addr[2];
manuf_data[ sizeof(manuf_data)-2] = bd_addr[1];
manuf_data[ sizeof(manuf_data)-1] = bd_addr[0];
/**
* Write Identity root key used to derive LTK and CSRK
*/
aci_hal_write_config_data(CONFIG_DATA_IR_OFFSET,
CONFIG_DATA_IR_LEN,
(uint8_t*) BLE_CFG_IR_VALUE);
/**
* Write Encryption root key used to derive LTK and CSRK
*/
aci_hal_write_config_data(CONFIG_DATA_ER_OFFSET,
CONFIG_DATA_ER_LEN,
(uint8_t*) BLE_CFG_ER_VALUE);
/**
* Write random bd_address
*/
/* random_bd_address = R_bd_address;
aci_hal_write_config_data(CONFIG_DATA_RANDOM_ADDRESS_WR,
CONFIG_DATA_RANDOM_ADDRESS_LEN,
(uint8_t*) random_bd_address);
*/
/**
* Static random Address
* The two upper bits shall be set to 1
* The lowest 32bits is read from the UDN to differentiate between devices
* The RNG may be used to provide a random number on each power on
*/
srd_bd_addr[1] = 0x0000ED6E;
srd_bd_addr[0] = LL_FLASH_GetUDN( );
aci_hal_write_config_data( CONFIG_DATA_RANDOM_ADDRESS_OFFSET, CONFIG_DATA_RANDOM_ADDRESS_LEN, (uint8_t*)srd_bd_addr );
/**
* Write Identity root key used to derive LTK and CSRK
*/
aci_hal_write_config_data( CONFIG_DATA_IR_OFFSET, CONFIG_DATA_IR_LEN, (uint8_t*)BLE_CFG_IR_VALUE );
/**
* Write Encryption root key used to derive LTK and CSRK
*/
aci_hal_write_config_data( CONFIG_DATA_ER_OFFSET, CONFIG_DATA_ER_LEN, (uint8_t*)BLE_CFG_ER_VALUE );
/**
* Set TX Power to 0dBm.
*/
aci_hal_set_tx_power_level(1, CFG_TX_POWER);
/**
* Initialize GATT interface
*/
aci_gatt_init();
/**
* Initialize GAP interface
*/
role = 0;
#if (BLE_CFG_PERIPHERAL == 1)
role |= GAP_PERIPHERAL_ROLE;
#endif
#if (BLE_CFG_CENTRAL == 1)
role |= GAP_CENTRAL_ROLE;
#endif
if (role > 0)
{
const char *name = furi_hal_version_get_device_name_ptr();
aci_gap_init(role, 0,
strlen(name),
&gap_service_handle, &gap_dev_name_char_handle, &gap_appearance_char_handle);
if (aci_gatt_update_char_value(gap_service_handle, gap_dev_name_char_handle, 0, strlen(name), (uint8_t *) name))
{
BLE_DBG_SVCCTL_MSG("Device Name aci_gatt_update_char_value failed.\r\n");
}
}
if(aci_gatt_update_char_value(gap_service_handle,
gap_appearance_char_handle,
0,
2,
(uint8_t *)&appearance))
{
BLE_DBG_SVCCTL_MSG("Appearance aci_gatt_update_char_value failed.\r\n");
}
/**
* Initialize Default PHY
*/
hci_le_set_default_phy(ALL_PHYS_PREFERENCE,TX_2M_PREFERRED,RX_2M_PREFERRED);
/**
* Initialize IO capability
*/
BleApplicationContext.BleApplicationContext_legacy.bleSecurityParam.ioCapability = CFG_IO_CAPABILITY;
aci_gap_set_io_capability(BleApplicationContext.BleApplicationContext_legacy.bleSecurityParam.ioCapability);
/**
* Initialize authentication
*/
BleApplicationContext.BleApplicationContext_legacy.bleSecurityParam.mitm_mode = CFG_MITM_PROTECTION;
BleApplicationContext.BleApplicationContext_legacy.bleSecurityParam.encryptionKeySizeMin = CFG_ENCRYPTION_KEY_SIZE_MIN;
BleApplicationContext.BleApplicationContext_legacy.bleSecurityParam.encryptionKeySizeMax = CFG_ENCRYPTION_KEY_SIZE_MAX;
BleApplicationContext.BleApplicationContext_legacy.bleSecurityParam.Use_Fixed_Pin = CFG_USED_FIXED_PIN;
BleApplicationContext.BleApplicationContext_legacy.bleSecurityParam.Fixed_Pin = CFG_FIXED_PIN;
BleApplicationContext.BleApplicationContext_legacy.bleSecurityParam.bonding_mode = CFG_BONDING_MODE;
aci_gap_set_authentication_requirement(BleApplicationContext.BleApplicationContext_legacy.bleSecurityParam.bonding_mode,
BleApplicationContext.BleApplicationContext_legacy.bleSecurityParam.mitm_mode,
CFG_SC_SUPPORT,
CFG_KEYPRESS_NOTIFICATION_SUPPORT,
BleApplicationContext.BleApplicationContext_legacy.bleSecurityParam.encryptionKeySizeMin,
BleApplicationContext.BleApplicationContext_legacy.bleSecurityParam.encryptionKeySizeMax,
BleApplicationContext.BleApplicationContext_legacy.bleSecurityParam.Use_Fixed_Pin,
BleApplicationContext.BleApplicationContext_legacy.bleSecurityParam.Fixed_Pin,
PUBLIC_ADDR
);
/**
* Initialize whitelist
*/
if (BleApplicationContext.BleApplicationContext_legacy.bleSecurityParam.bonding_mode)
{
aci_gap_configure_whitelist();
}
}
static void Adv_Request(APP_BLE_ConnStatus_t New_Status)
{
tBleStatus ret = BLE_STATUS_INVALID_PARAMS;
uint16_t Min_Inter, Max_Inter;
if (New_Status == APP_BLE_FAST_ADV)
{
Min_Inter = AdvIntervalMin;
Max_Inter = AdvIntervalMax;
}
else
{
Min_Inter = CFG_LP_CONN_ADV_INTERVAL_MIN;
Max_Inter = CFG_LP_CONN_ADV_INTERVAL_MAX;
}
/**
* Stop the timer, it will be restarted for a new shot
* It does not hurt if the timer was not running
*/
HW_TS_Stop(BleApplicationContext.Advertising_mgr_timer_Id);
APP_DBG_MSG("First index in %d state \r\n", BleApplicationContext.Device_Connection_Status);
if ((New_Status == APP_BLE_LP_ADV)
&& ((BleApplicationContext.Device_Connection_Status == APP_BLE_FAST_ADV)
|| (BleApplicationContext.Device_Connection_Status == APP_BLE_LP_ADV)))
{
/* Connection in ADVERTISE mode have to stop the current advertising */
ret = aci_gap_set_non_discoverable();
if (ret == BLE_STATUS_SUCCESS)
{
APP_DBG_MSG("Successfully Stopped Advertising \r\n");
}
else
{
APP_DBG_MSG("Stop Advertising Failed , result: %d \r\n", ret);
}
}
BleApplicationContext.Device_Connection_Status = New_Status;
const char* name = furi_hal_version_get_ble_local_device_name_ptr();
/* Start Fast or Low Power Advertising */
ret = aci_gap_set_discoverable(
ADV_IND,
Min_Inter,
Max_Inter,
PUBLIC_ADDR,
NO_WHITE_LIST_USE, /* use white list */
strlen(name),
(uint8_t*)name,
BleApplicationContext.BleApplicationContext_legacy.advtServUUIDlen,
BleApplicationContext.BleApplicationContext_legacy.advtServUUID,
0,
0);
/* Update Advertising data */
ret = aci_gap_update_adv_data(sizeof(manuf_data), (uint8_t*) manuf_data);
if (ret == BLE_STATUS_SUCCESS) {
if (New_Status == APP_BLE_FAST_ADV) {
APP_DBG_MSG("Successfully Start Fast Advertising \r\n" );
/* Start Timer to STOP ADV - TIMEOUT */
HW_TS_Start(BleApplicationContext.Advertising_mgr_timer_Id, INITIAL_ADV_TIMEOUT);
} else {
APP_DBG_MSG("Successfully Start Low Power Advertising \r\n");
}
} else {
if (New_Status == APP_BLE_FAST_ADV) {
APP_DBG_MSG("Start Fast Advertising Failed , result: %d \r\n", ret);
} else {
APP_DBG_MSG("Start Low Power Advertising Failed , result: %d \r\n", ret);
}
}
}
const uint8_t* BleGetBdAddress( void ) {
uint8_t *otp_addr;
const uint8_t *bd_addr;
uint32_t udn;
uint32_t company_id;
uint32_t device_id;
udn = LL_FLASH_GetUDN();
if(udn != 0xFFFFFFFF) {
company_id = LL_FLASH_GetSTCompanyID();
device_id = LL_FLASH_GetDeviceID();
bd_addr_udn[0] = (uint8_t)(udn & 0x000000FF);
bd_addr_udn[1] = (uint8_t)( (udn & 0x0000FF00) >> 8 );
bd_addr_udn[2] = (uint8_t)( (udn & 0x00FF0000) >> 16 );
bd_addr_udn[3] = (uint8_t)device_id;
bd_addr_udn[4] = (uint8_t)(company_id & 0x000000FF);;
bd_addr_udn[5] = (uint8_t)( (company_id & 0x0000FF00) >> 8 );
bd_addr = (const uint8_t *)bd_addr_udn;
} else {
otp_addr = OTP_Read(0);
if(otp_addr) {
bd_addr = ((OTP_ID0_t*)otp_addr)->bd_address;
} else {
bd_addr = M_bd_addr;
}
}
return bd_addr;
}
/*************************************************************
*
*SPECIFIC FUNCTIONS
*
*************************************************************/
static void Add_Advertisment_Service_UUID( uint16_t servUUID ) {
BleApplicationContext.BleApplicationContext_legacy.advtServUUID[BleApplicationContext.BleApplicationContext_legacy.advtServUUIDlen] =
(uint8_t) (servUUID & 0xFF);
BleApplicationContext.BleApplicationContext_legacy.advtServUUIDlen++;
BleApplicationContext.BleApplicationContext_legacy.advtServUUID[BleApplicationContext.BleApplicationContext_legacy.advtServUUIDlen] =
(uint8_t) (servUUID >> 8) & 0xFF;
BleApplicationContext.BleApplicationContext_legacy.advtServUUIDlen++;
}
static void Adv_Mgr( void ) {
/**
* The code shall be executed in the background as an aci command may be sent
* The background is the only place where the application can make sure a new aci command
* is not sent if there is a pending one
*/
osThreadFlagsSet( AdvUpdateProcessId, 1 );
}
static void AdvUpdateProcess(void *argument) {
UNUSED(argument);
for(;;) {
osThreadFlagsWait( 1, osFlagsWaitAny, osWaitForever);
Adv_Update( );
}
}
static void Adv_Update( void ) {
Adv_Request(APP_BLE_LP_ADV);
}
static void HciUserEvtProcess(void *argument) {
UNUSED(argument);

View File

@ -7,24 +7,7 @@ extern "C" {
#include <stdbool.h>
#include "hci_tl.h"
typedef enum {
APP_BLE_IDLE,
APP_BLE_FAST_ADV,
APP_BLE_LP_ADV,
APP_BLE_SCAN,
APP_BLE_LP_CONNECTING,
APP_BLE_CONNECTED_SERVER,
APP_BLE_CONNECTED_CLIENT
} APP_BLE_ConnStatus_t;
bool APP_BLE_Init();
bool APP_BLE_Start();
APP_BLE_ConnStatus_t APP_BLE_Get_Server_Connection_Status();
void APP_BLE_Key_Button1_Action();
void APP_BLE_Key_Button2_Action();
void APP_BLE_Key_Button3_Action();
#ifdef __cplusplus
}

View File

@ -32,81 +32,12 @@ extern "C"{
#include <stdlib.h>
#include <stdarg.h>
#include <furi/common_defines.h>
#include "app_conf.h"
/* -------------------------------- *
* Basic definitions *
* -------------------------------- */
#undef NULL
#define NULL 0
#undef FALSE
#define FALSE 0
#undef TRUE
#define TRUE (!0)
/* -------------------------------- *
* Critical Section definition *
* -------------------------------- */
#define BACKUP_PRIMASK() uint32_t primask_bit= __get_PRIMASK()
#define DISABLE_IRQ() __disable_irq()
#define RESTORE_PRIMASK() __set_PRIMASK(primask_bit)
/* -------------------------------- *
* Macro delimiters *
* -------------------------------- */
#define M_BEGIN do {
#define M_END } while(0)
/* -------------------------------- *
* Some useful macro definitions *
* -------------------------------- */
#define MODINC( a, m ) M_BEGIN (a)++; if ((a)>=(m)) (a)=0; M_END
#define MODDEC( a, m ) M_BEGIN if ((a)==0) (a)=(m); (a)--; M_END
#define MODADD( a, b, m ) M_BEGIN (a)+=(b); if ((a)>=(m)) (a)-=(m); M_END
#define MODSUB( a, b, m ) MODADD( a, (m)-(b), m )
#define PAUSE( t ) M_BEGIN \
__IO int _i; \
for ( _i = t; _i > 0; _i -- ); \
M_END
#define DIVF( x, y ) ((x)/(y))
#define DIVC( x, y ) (((x)+(y)-1)/(y))
#define DIVR( x, y ) (((x)+((y)/2))/(y))
#define SHRR( x, n ) ((((x)>>((n)-1))+1)>>1)
#define BITN( w, n ) (((w)[(n)/32] >> ((n)%32)) & 1)
#define BITNSET( w, n, b ) M_BEGIN (w)[(n)/32] |= ((U32)(b))<<((n)%32); M_END
/* -------------------------------- *
* Compiler *
* -------------------------------- */
#define PLACE_IN_SECTION( __x__ ) __attribute__((section (__x__)))
#ifdef WIN32
#define ALIGN(n)
#else
#define ALIGN(n) __attribute__((aligned(n)))
#endif
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif /*APP_COMMON_H */
/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
#endif

View File

@ -139,7 +139,7 @@
/**
* Maximum supported ATT_MTU size
*/
#define CFG_BLE_MAX_ATT_MTU (156)
#define CFG_BLE_MAX_ATT_MTU (251)
/**
* Size of the storage area for Attribute values

View File

@ -1,367 +0,0 @@
/* USER CODE BEGIN Header */
/**
******************************************************************************
* File Name : app_debug.c
* Description : Debug capabilities source file for STM32WPAN Middleware
******************************************************************************
* @attention
*
* <h2><center>&copy; Copyright (c) 2020 STMicroelectronics.
* All rights reserved.</center></h2>
*
* This software component is licensed by ST under Ultimate Liberty license
* SLA0044, the "License"; You may not use this file except in compliance with
* the License. You may obtain a copy of the License at:
* www.st.com/SLA0044
*
******************************************************************************
*/
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include "app_common.h"
#include "app_debug.h"
#include "utilities_common.h"
#include "shci.h"
#include "tl.h"
#include "dbg_trace.h"
#include <furi-hal.h>
/* USER CODE END Includes */
/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */
typedef PACKED_STRUCT
{
GPIO_TypeDef* port;
uint16_t pin;
uint8_t enable;
uint8_t reserved;
} APPD_GpioConfig_t;
/* USER CODE END PTD */
/* Private defines -----------------------------------------------------------*/
/* USER CODE BEGIN PD */
#define GPIO_NBR_OF_RF_SIGNALS 9
#define GPIO_CFG_NBR_OF_FEATURES 34
#define NBR_OF_TRACES_CONFIG_PARAMETERS 4
#define NBR_OF_GENERAL_CONFIG_PARAMETERS 4
/**
* THIS SHALL BE SET TO A VALUE DIFFERENT FROM 0 ONLY ON REQUEST FROM ST SUPPORT
*/
#define BLE_DTB_CFG 0
#define SYS_DBG_CFG1 (SHCI_C2_DEBUG_OPTIONS_IPCORE_LP | SHCI_C2_DEBUG_OPTIONS_CPU2_STOP_EN)
/* USER CODE END PD */
/* Private variables ---------------------------------------------------------*/
/* USER CODE BEGIN PV */
PLACE_IN_SECTION("MB_MEM2") ALIGN(4) static SHCI_C2_DEBUG_TracesConfig_t APPD_TracesConfig={0, 0, 0, 0};
PLACE_IN_SECTION("MB_MEM2") ALIGN(4) static SHCI_C2_DEBUG_GeneralConfig_t APPD_GeneralConfig={BLE_DTB_CFG, SYS_DBG_CFG1, {0, 0}};
/**
* THE DEBUG ON GPIO FOR CPU2 IS INTENDED TO BE USED ONLY ON REQUEST FROM ST SUPPORT
* It provides timing information on the CPU2 activity.
* All configuration of (port, pin) is supported for each features and can be selected by the user
* depending on the availability
*/
static const APPD_GpioConfig_t aGpioConfigList[GPIO_CFG_NBR_OF_FEATURES] =
{
{ GPIOA, LL_GPIO_PIN_0, 0, 0}, /* BLE_ISR - Set on Entry / Reset on Exit */
{ GPIOA, LL_GPIO_PIN_0, 0, 0}, /* BLE_STACK_TICK - Set on Entry / Reset on Exit */
{ GPIOA, LL_GPIO_PIN_0, 0, 0}, /* BLE_CMD_PROCESS - Set on Entry / Reset on Exit */
{ GPIOA, LL_GPIO_PIN_0, 0, 0}, /* BLE_ACL_DATA_PROCESS - Set on Entry / Reset on Exit */
{ GPIOA, LL_GPIO_PIN_0, 0, 0}, /* SYS_CMD_PROCESS - Set on Entry / Reset on Exit */
{ GPIOA, LL_GPIO_PIN_0, 0, 0}, /* RNG_PROCESS - Set on Entry / Reset on Exit */
{ GPIOA, LL_GPIO_PIN_0, 0, 0}, /* NVM_PROCESS - Set on Entry / Reset on Exit */
{ GPIOA, LL_GPIO_PIN_0, 0, 0}, /* IPCC_GENERAL - Set on Entry / Reset on Exit */
{ GPIOA, LL_GPIO_PIN_0, 0, 0}, /* IPCC_BLE_CMD_RX - Set on Entry / Reset on Exit */
{ GPIOA, LL_GPIO_PIN_0, 0, 0}, /* IPCC_BLE_EVT_TX - Set on Entry / Reset on Exit */
{ GPIOA, LL_GPIO_PIN_0, 0, 0}, /* IPCC_BLE_ACL_DATA_RX - Set on Entry / Reset on Exit */
{ GPIOA, LL_GPIO_PIN_0, 0, 0}, /* IPCC_SYS_CMD_RX - Set on Entry / Reset on Exit */
{ GPIOA, LL_GPIO_PIN_0, 0, 0}, /* IPCC_SYS_EVT_TX - Set on Entry / Reset on Exit */
{ GPIOA, LL_GPIO_PIN_0, 0, 0}, /* IPCC_CLI_CMD_RX - Set on Entry / Reset on Exit */
{ GPIOA, LL_GPIO_PIN_0, 0, 0}, /* IPCC_OT_CMD_RX - Set on Entry / Reset on Exit */
{ GPIOA, LL_GPIO_PIN_0, 0, 0}, /* IPCC_OT_ACK_TX - Set on Entry / Reset on Exit */
{ GPIOA, LL_GPIO_PIN_0, 0, 0}, /* IPCC_CLI_ACK_TX - Set on Entry / Reset on Exit */
{ GPIOA, LL_GPIO_PIN_0, 0, 0}, /* IPCC_MEM_MANAGER_RX - Set on Entry / Reset on Exit */
{ GPIOA, LL_GPIO_PIN_0, 0, 0}, /* IPCC_TRACES_TX - Set on Entry / Reset on Exit */
{ GPIOA, LL_GPIO_PIN_0, 0, 0}, /* HARD_FAULT - Set on Entry / Reset on Exit */
/* From v1.1.1 */
{ GPIOA, LL_GPIO_PIN_0, 0, 0}, /* IP_CORE_LP_STATUS - Set on Entry / Reset on Exit */
/* From v1.2.0 */
{ GPIOA, LL_GPIO_PIN_0, 0, 0}, /* END_OF_CONNECTION_EVENT - Set on Entry / Reset on Exit */
{ GPIOA, LL_GPIO_PIN_0, 0, 0}, /* TIMER_SERVER_CALLBACK - Toggle on Entry */
{ GPIOA, LL_GPIO_PIN_0, 0, 0}, /* PES_ACTIVITY - Set on Entry / Reset on Exit */
{ GPIOA, LL_GPIO_PIN_0, 0, 0}, /* MB_BLE_SEND_EVT - Set on Entry / Reset on Exit */
/* From v1.3.0 */
{ GPIOA, LL_GPIO_PIN_0, 0, 0}, /* BLE_NO_DELAY - Set on Entry / Reset on Exit */
{ GPIOA, LL_GPIO_PIN_0, 0, 0}, /* BLE_STACK_STORE_NVM_CB - Set on Entry / Reset on Exit */
{ GPIOA, LL_GPIO_PIN_0, 0, 0}, /* NVMA_WRITE_ONGOING - Set on Entry / Reset on Exit */
{ GPIOA, LL_GPIO_PIN_0, 0, 0}, /* NVMA_WRITE_COMPLETE - Set on Entry / Reset on Exit */
{ GPIOA, LL_GPIO_PIN_0, 0, 0}, /* NVMA_CLEANUP - Set on Entry / Reset on Exit */
/* From v1.4.0 */
{ GPIOA, LL_GPIO_PIN_0, 0, 0}, /* NVMA_START - Set on Entry / Reset on Exit */
{ GPIOA, LL_GPIO_PIN_0, 0, 0}, /* FLASH_EOP - Set on Entry / Reset on Exit */
{ GPIOA, LL_GPIO_PIN_0, 0, 0}, /* FLASH_WRITE - Set on Entry / Reset on Exit */
{ GPIOA, LL_GPIO_PIN_0, 0, 0}, /* FLASH_ERASE - Set on Entry / Reset on Exit */
};
/**
* THE DEBUG ON GPIO FOR CPU2 IS INTENDED TO BE USED ONLY ON REQUEST FROM ST SUPPORT
* This table is relevant only for BLE
* It provides timing information on BLE RF activity.
* New signals may be allocated at any location when requested by ST
* The GPIO allocated to each signal depend on the BLE_DTB_CFG value and cannot be changed
*/
#if( BLE_DTB_CFG == 7)
static const APPD_GpioConfig_t aRfConfigList[GPIO_NBR_OF_RF_SIGNALS] =
{
{ GPIOB, LL_GPIO_PIN_2, 0, 0}, /* DTB10 - Tx/Rx SPI */
{ GPIOB, LL_GPIO_PIN_7, 0, 0}, /* DTB11 - Tx/Tx SPI Clk */
{ GPIOA, LL_GPIO_PIN_8, 0, 0}, /* DTB12 - Tx/Rx Ready & SPI Select */
{ GPIOA, LL_GPIO_PIN_9, 0, 0}, /* DTB13 - Tx/Rx Start */
{ GPIOA, LL_GPIO_PIN_10, 0, 0}, /* DTB14 - FSM0 */
{ GPIOA, LL_GPIO_PIN_11, 0, 0}, /* DTB15 - FSM1 */
{ GPIOB, LL_GPIO_PIN_8, 0, 0}, /* DTB16 - FSM2 */
{ GPIOB, LL_GPIO_PIN_11, 0, 0}, /* DTB17 - FSM3 */
{ GPIOB, LL_GPIO_PIN_10, 0, 0}, /* DTB18 - FSM4 */
};
#endif
/* USER CODE END PV */
/* Global variables ----------------------------------------------------------*/
/* USER CODE BEGIN GV */
/* USER CODE END GV */
/* Private function prototypes -----------------------------------------------*/
/* USER CODE BEGIN PFP */
static void APPD_SetCPU2GpioConfig( void );
static void APPD_BleDtbCfg( void );
/* USER CODE END PFP */
/* Functions Definition ------------------------------------------------------*/
void APPD_Init( void )
{
/* USER CODE BEGIN APPD_Init */
#if (CFG_DEBUGGER_SUPPORTED == 1)
/**
* Keep debugger enabled while in any low power mode
*/
HAL_DBGMCU_EnableDBGSleepMode();
HAL_DBGMCU_EnableDBGStopMode();
/***************** ENABLE DEBUGGER *************************************/
LL_EXTI_EnableIT_32_63(LL_EXTI_LINE_48);
#else
GPIO_InitTypeDef gpio_config = {0};
gpio_config.Pull = GPIO_NOPULL;
gpio_config.Mode = GPIO_MODE_ANALOG;
gpio_config.Pin = GPIO_PIN_15 | GPIO_PIN_14 | GPIO_PIN_13;
__HAL_RCC_GPIOA_CLK_ENABLE();
HAL_GPIO_Init(GPIOA, &gpio_config);
__HAL_RCC_GPIOA_CLK_DISABLE();
gpio_config.Pin = GPIO_PIN_4 | GPIO_PIN_3;
__HAL_RCC_GPIOB_CLK_ENABLE();
HAL_GPIO_Init(GPIOB, &gpio_config);
__HAL_RCC_GPIOB_CLK_DISABLE();
HAL_DBGMCU_DisableDBGSleepMode();
HAL_DBGMCU_DisableDBGStopMode();
HAL_DBGMCU_DisableDBGStandbyMode();
#endif /* (CFG_DEBUGGER_SUPPORTED == 1) */
#if(CFG_DEBUG_TRACE != 0)
DbgTraceInit();
#endif
APPD_SetCPU2GpioConfig( );
APPD_BleDtbCfg( );
/* USER CODE END APPD_Init */
return;
}
void APPD_EnableCPU2( void )
{
/* USER CODE BEGIN APPD_EnableCPU2 */
SHCI_C2_DEBUG_Init_Cmd_Packet_t DebugCmdPacket =
{
{{0,0,0}}, /**< Does not need to be initialized */
{(uint8_t *)aGpioConfigList,
(uint8_t *)&APPD_TracesConfig,
(uint8_t *)&APPD_GeneralConfig,
GPIO_CFG_NBR_OF_FEATURES,
NBR_OF_TRACES_CONFIG_PARAMETERS,
NBR_OF_GENERAL_CONFIG_PARAMETERS}
};
/**< Traces channel initialization */
TL_TRACES_Init( );
/** GPIO DEBUG Initialization */
SHCI_C2_DEBUG_Init( &DebugCmdPacket );
/* USER CODE END APPD_EnableCPU2 */
return;
}
/*************************************************************
*
* LOCAL FUNCTIONS
*
*************************************************************/
static void APPD_SetCPU2GpioConfig( void )
{
/* USER CODE BEGIN APPD_SetCPU2GpioConfig */
GPIO_InitTypeDef gpio_config = {0};
uint8_t local_loop;
uint16_t gpioa_pin_list;
uint16_t gpiob_pin_list;
uint16_t gpioc_pin_list;
gpioa_pin_list = 0;
gpiob_pin_list = 0;
gpioc_pin_list = 0;
for(local_loop = 0 ; local_loop < GPIO_CFG_NBR_OF_FEATURES; local_loop++)
{
if( aGpioConfigList[local_loop].enable != 0)
{
switch((uint32_t)aGpioConfigList[local_loop].port)
{
case (uint32_t)GPIOA:
gpioa_pin_list |= aGpioConfigList[local_loop].pin;
break;
case (uint32_t)GPIOB:
gpiob_pin_list |= aGpioConfigList[local_loop].pin;
break;
case (uint32_t)GPIOC:
gpioc_pin_list |= aGpioConfigList[local_loop].pin;
break;
default:
break;
}
}
}
gpio_config.Pull = GPIO_NOPULL;
gpio_config.Mode = GPIO_MODE_OUTPUT_PP;
gpio_config.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
if(gpioa_pin_list != 0)
{
gpio_config.Pin = gpioa_pin_list;
__HAL_RCC_GPIOA_CLK_ENABLE();
__HAL_RCC_C2GPIOA_CLK_ENABLE();
HAL_GPIO_Init(GPIOA, &gpio_config);
HAL_GPIO_WritePin(GPIOA, gpioa_pin_list, GPIO_PIN_RESET);
}
if(gpiob_pin_list != 0)
{
gpio_config.Pin = gpiob_pin_list;
__HAL_RCC_GPIOB_CLK_ENABLE();
__HAL_RCC_C2GPIOB_CLK_ENABLE();
HAL_GPIO_Init(GPIOB, &gpio_config);
HAL_GPIO_WritePin(GPIOB, gpiob_pin_list, GPIO_PIN_RESET);
}
if(gpioc_pin_list != 0)
{
gpio_config.Pin = gpioc_pin_list;
__HAL_RCC_GPIOC_CLK_ENABLE();
__HAL_RCC_C2GPIOC_CLK_ENABLE();
HAL_GPIO_Init(GPIOC, &gpio_config);
HAL_GPIO_WritePin(GPIOC, gpioc_pin_list, GPIO_PIN_RESET);
}
/* USER CODE END APPD_SetCPU2GpioConfig */
return;
}
static void APPD_BleDtbCfg( void )
{
/* USER CODE BEGIN APPD_BleDtbCfg */
#if (BLE_DTB_CFG != 0)
GPIO_InitTypeDef gpio_config = {0};
uint8_t local_loop;
uint16_t gpioa_pin_list;
uint16_t gpiob_pin_list;
gpioa_pin_list = 0;
gpiob_pin_list = 0;
for(local_loop = 0 ; local_loop < GPIO_NBR_OF_RF_SIGNALS; local_loop++)
{
if( aRfConfigList[local_loop].enable != 0)
{
switch((uint32_t)aRfConfigList[local_loop].port)
{
case (uint32_t)GPIOA:
gpioa_pin_list |= aRfConfigList[local_loop].pin;
break;
case (uint32_t)GPIOB:
gpiob_pin_list |= aRfConfigList[local_loop].pin;
break;
default:
break;
}
}
}
gpio_config.Pull = GPIO_NOPULL;
gpio_config.Mode = GPIO_MODE_AF_PP;
gpio_config.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
gpio_config.Alternate = GPIO_AF6_RF_DTB7;
if(gpioa_pin_list != 0)
{
gpio_config.Pin = gpioa_pin_list;
__HAL_RCC_GPIOA_CLK_ENABLE();
__HAL_RCC_C2GPIOA_CLK_ENABLE();
HAL_GPIO_Init(GPIOA, &gpio_config);
}
if(gpiob_pin_list != 0)
{
gpio_config.Pin = gpiob_pin_list;
__HAL_RCC_GPIOB_CLK_ENABLE();
__HAL_RCC_C2GPIOB_CLK_ENABLE();
HAL_GPIO_Init(GPIOB, &gpio_config);
}
#endif
/* USER CODE END APPD_BleDtbCfg */
return;
}
/*************************************************************
*
* WRAP FUNCTIONS
*
*************************************************************/
#if(CFG_DEBUG_TRACE != 0)
void DbgOutputInit( void )
{
}
void DbgOutputTraces( uint8_t *p_data, uint16_t size, void (*cb)(void) )
{
furi_hal_console_tx(p_data, size);
cb();
}
#endif
/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/

View File

@ -6,7 +6,6 @@
#include "tl.h"
#include "cmsis_os.h"
#include "shci_tl.h"
#include "app_debug.h"
#include <furi-hal.h>
extern RTC_HandleTypeDef hrtc;
@ -139,8 +138,10 @@ static void APPE_SysUserEvtRx( void * pPayload ) {
// APPD_EnableCPU2( );
if (APP_BLE_Init()) {
FURI_LOG_I("Core2", "BLE stack started");
ble_glue_status = BleGlueStatusStarted;
} else {
FURI_LOG_E("Core2", "BLE stack startup failed");
ble_glue_status = BleGlueStatusBroken;
}
furi_hal_power_insomnia_exit();
@ -178,3 +179,16 @@ void shci_cmd_resp_wait(uint32_t timeout) {
UNUSED(timeout);
osSemaphoreAcquire( SemShciId, osWaitForever );
}
#if(CFG_DEBUG_TRACE != 0)
void DbgOutputInit( void )
{
}
void DbgOutputTraces( uint8_t *p_data, uint16_t size, void (*cb)(void) )
{
furi_hal_console_tx(p_data, size);
cb();
}
#endif

View File

@ -0,0 +1,78 @@
#include "battery_service.h"
#include "app_common.h"
#include "ble.h"
#include <furi.h>
#define BATTERY_SERVICE_TAG "battery service"
typedef struct {
uint16_t svc_handle;
uint16_t char_level_handle;
} BatterySvc;
static BatterySvc* battery_svc = NULL;
static const uint16_t service_uuid = BATTERY_SERVICE_UUID;
static const uint16_t char_battery_level_uuid = BATTERY_LEVEL_CHAR_UUID;
void battery_svc_start() {
battery_svc = furi_alloc(sizeof(BatterySvc));
tBleStatus status;
// Add Battery service
status = aci_gatt_add_service(UUID_TYPE_16, (Service_UUID_t*)&service_uuid, PRIMARY_SERVICE, 4, &battery_svc->svc_handle);
if(status) {
FURI_LOG_E(BATTERY_SERVICE_TAG, "Failed to add Battery service: %d", status);
}
// Add Battery level characteristic
status = aci_gatt_add_char(battery_svc->svc_handle,
UUID_TYPE_16,
(Char_UUID_t *) &char_battery_level_uuid,
1,
CHAR_PROP_READ | CHAR_PROP_NOTIFY,
ATTR_PERMISSION_NONE,
GATT_DONT_NOTIFY_EVENTS,
10,
CHAR_VALUE_LEN_CONSTANT,
&battery_svc->char_level_handle);
if(status) {
FURI_LOG_E(BATTERY_SERVICE_TAG, "Failed to add Battery level characteristic: %d", status);
}
}
void battery_svc_stop() {
tBleStatus status;
if(battery_svc) {
// Delete Battery level characteristic
status = aci_gatt_del_char(battery_svc->svc_handle, battery_svc->char_level_handle);
if(status) {
FURI_LOG_E(BATTERY_SERVICE_TAG, "Failed to delete Battery level characteristic: %d", status);
}
// Delete Battery service
status = aci_gatt_del_service(battery_svc->svc_handle);
if(status) {
FURI_LOG_E(BATTERY_SERVICE_TAG, "Failed to delete Battery service: %d", status);
}
free(battery_svc);
battery_svc = NULL;
}
}
bool battery_svc_update_level(uint8_t battery_charge) {
// Check if service was started
if(battery_svc == NULL) {
return false;
}
// Update battery level characteristic
FURI_LOG_I(BATTERY_SERVICE_TAG, "Updating battery level characteristic");
tBleStatus result = aci_gatt_update_char_value(battery_svc->svc_handle,
battery_svc->char_level_handle,
0,
1,
&battery_charge);
if(result) {
FURI_LOG_E(BATTERY_SERVICE_TAG, "Failed updating RX characteristic: %d", result);
}
return result != BLE_STATUS_SUCCESS;
}

View File

@ -0,0 +1,18 @@
#pragma once
#include <stdint.h>
#include <stdbool.h>
#ifdef __cplusplus
extern "C" {
#endif
void battery_svc_start();
void battery_svc_stop();
bool battery_svc_update_level(uint8_t battery_level);
#ifdef __cplusplus
}
#endif

View File

@ -53,47 +53,12 @@
#define BLE_CFG_CLT_MAX_NBR_CB 0
/******************************************************************************
* Device Information Service (DIS)
******************************************************************************/
/**< Options: Supported(1) or Not Supported(0) */
#define BLE_CFG_DIS_MANUFACTURER_NAME_STRING 1
#define BLE_CFG_DIS_MODEL_NUMBER_STRING 1
#define BLE_CFG_DIS_SERIAL_NUMBER_STRING 0
#define BLE_CFG_DIS_HARDWARE_REVISION_STRING 0
#define BLE_CFG_DIS_FIRMWARE_REVISION_STRING 1
#define BLE_CFG_DIS_SOFTWARE_REVISION_STRING 1
#define BLE_CFG_DIS_SYSTEM_ID 0
#define BLE_CFG_DIS_IEEE_CERTIFICATION 0
#define BLE_CFG_DIS_PNP_ID 0
/**
* device information service characteristic lengths
*/
#define BLE_CFG_DIS_SYSTEM_ID_LEN_MAX (8)
#define BLE_CFG_DIS_MODEL_NUMBER_STRING_LEN_MAX (32)
#define BLE_CFG_DIS_SERIAL_NUMBER_STRING_LEN_MAX (32)
#define BLE_CFG_DIS_FIRMWARE_REVISION_STRING_LEN_MAX (32)
#define BLE_CFG_DIS_HARDWARE_REVISION_STRING_LEN_MAX (32)
#define BLE_CFG_DIS_SOFTWARE_REVISION_STRING_LEN_MAX (64)
#define BLE_CFG_DIS_MANUFACTURER_NAME_STRING_LEN_MAX (32)
#define BLE_CFG_DIS_IEEE_CERTIFICATION_LEN_MAX (32)
#define BLE_CFG_DIS_PNP_ID_LEN_MAX (7)
/******************************************************************************
* Heart Rate Service (HRS)
******************************************************************************/
#define BLE_CFG_HRS_BODY_SENSOR_LOCATION_CHAR 1/**< BODY SENSOR LOCATION CHARACTERISTIC */
#define BLE_CFG_HRS_ENERGY_EXPENDED_INFO_FLAG 1/**< ENERGY EXTENDED INFO FLAG */
#define BLE_CFG_HRS_ENERGY_RR_INTERVAL_FLAG 1/**< Max number of RR interval values - Shall not be greater than 9 */
/******************************************************************************
* GAP Service - Apprearance
******************************************************************************/
#define BLE_CFG_UNKNOWN_APPEARANCE (0)
#define BLE_CFG_HR_SENSOR_APPEARANCE (832)
#define BLE_CFG_GAP_APPEARANCE (BLE_CFG_HR_SENSOR_APPEARANCE)
#define BLE_CFG_GAP_APPEARANCE (0x0086)
/******************************************************************************
* Over The Air Feature (OTA) - STM Proprietary

View File

@ -0,0 +1,156 @@
#include "dev_info_service.h"
#include "app_common.h"
#include "ble.h"
#include <furi.h>
#define DEV_INFO_SVC_TAG "dev info service"
typedef struct {
uint16_t service_handle;
uint16_t man_name_char_handle;
uint16_t serial_num_char_handle;
uint16_t firmware_rev_char_handle;
uint16_t software_rev_char_handle;
} DevInfoSvc;
static DevInfoSvc* dev_info_svc = NULL;
static const char dev_info_man_name[] = "Flipper Devices Inc.";
static const char dev_info_serial_num[] = "1.0";
static const char dev_info_firmware_rev_num[] = TARGET;
static const char dev_info_software_rev_num[] = GIT_COMMIT " " GIT_BRANCH " " GIT_BRANCH_NUM " " BUILD_DATE;
void dev_info_svc_start() {
dev_info_svc = furi_alloc(sizeof(DevInfoSvc));
tBleStatus status;
// Add Device Information Service
uint16_t uuid = DEVICE_INFORMATION_SERVICE_UUID;
status = aci_gatt_add_service(UUID_TYPE_16, (Service_UUID_t*)&uuid, PRIMARY_SERVICE, 9, &dev_info_svc->service_handle);
if(status) {
FURI_LOG_E(DEV_INFO_SVC_TAG, "Failed to add Device Information Service: %d", status);
}
// Add characteristics
uuid = MANUFACTURER_NAME_UUID;
status = aci_gatt_add_char(dev_info_svc->service_handle,
UUID_TYPE_16,
(Char_UUID_t*)&uuid,
strlen(dev_info_man_name),
CHAR_PROP_READ,
ATTR_PERMISSION_NONE,
GATT_DONT_NOTIFY_EVENTS,
10,
CHAR_VALUE_LEN_CONSTANT,
&dev_info_svc->man_name_char_handle);
if(status) {
FURI_LOG_E(DEV_INFO_SVC_TAG, "Failed to add manufacturer name char: %d", status);
}
uuid = SERIAL_NUMBER_UUID;
status = aci_gatt_add_char(dev_info_svc->service_handle,
UUID_TYPE_16,
(Char_UUID_t*)&uuid,
strlen(dev_info_serial_num),
CHAR_PROP_READ,
ATTR_PERMISSION_NONE,
GATT_DONT_NOTIFY_EVENTS,
10,
CHAR_VALUE_LEN_CONSTANT,
&dev_info_svc->serial_num_char_handle);
if(status) {
FURI_LOG_E(DEV_INFO_SVC_TAG, "Failed to add serial number char: %d", status);
}
uuid = FIRMWARE_REVISION_UUID;
status = aci_gatt_add_char(dev_info_svc->service_handle,
UUID_TYPE_16,
(Char_UUID_t*)&uuid,
strlen(dev_info_firmware_rev_num),
CHAR_PROP_READ,
ATTR_PERMISSION_NONE,
GATT_DONT_NOTIFY_EVENTS,
10,
CHAR_VALUE_LEN_CONSTANT,
&dev_info_svc->firmware_rev_char_handle);
if(status) {
FURI_LOG_E(DEV_INFO_SVC_TAG, "Failed to add firmware revision char: %d", status);
}
uuid = SOFTWARE_REVISION_UUID;
status = aci_gatt_add_char(dev_info_svc->service_handle,
UUID_TYPE_16,
(Char_UUID_t*)&uuid,
strlen(dev_info_software_rev_num),
CHAR_PROP_READ,
ATTR_PERMISSION_NONE,
GATT_DONT_NOTIFY_EVENTS,
10,
CHAR_VALUE_LEN_CONSTANT,
&dev_info_svc->software_rev_char_handle);
if(status) {
FURI_LOG_E(DEV_INFO_SVC_TAG, "Failed to add software revision char: %d", status);
}
// Update characteristics
status = aci_gatt_update_char_value(dev_info_svc->service_handle,
dev_info_svc->man_name_char_handle,
0,
strlen(dev_info_man_name),
(uint8_t*)dev_info_man_name);
if(status) {
FURI_LOG_E(DEV_INFO_SVC_TAG, "Failed to update manufacturer name char: %d", status);
}
status = aci_gatt_update_char_value(dev_info_svc->service_handle,
dev_info_svc->serial_num_char_handle,
0,
strlen(dev_info_serial_num),
(uint8_t*)dev_info_serial_num);
if(status) {
FURI_LOG_E(DEV_INFO_SVC_TAG, "Failed to update serial number char: %d", status);
}
status = aci_gatt_update_char_value(dev_info_svc->service_handle,
dev_info_svc->firmware_rev_char_handle,
0,
strlen(dev_info_firmware_rev_num),
(uint8_t*)dev_info_firmware_rev_num);
if(status) {
FURI_LOG_E(DEV_INFO_SVC_TAG, "Failed to update firmware revision char: %d", status);
}
status = aci_gatt_update_char_value(dev_info_svc->service_handle,
dev_info_svc->software_rev_char_handle,
0,
strlen(dev_info_software_rev_num),
(uint8_t*)dev_info_software_rev_num);
if(status) {
FURI_LOG_E(DEV_INFO_SVC_TAG, "Failed to update software revision char: %d", status);
}
}
void dev_info_svc_stop() {
tBleStatus status;
if(dev_info_svc) {
// Delete service characteristics
status = aci_gatt_del_char(dev_info_svc->service_handle, dev_info_svc->man_name_char_handle);
if(status) {
FURI_LOG_E(DEV_INFO_SVC_TAG, "Failed to delete manufacturer name char: %d", status);
}
status = aci_gatt_del_char(dev_info_svc->service_handle, dev_info_svc->serial_num_char_handle);
if(status) {
FURI_LOG_E(DEV_INFO_SVC_TAG, "Failed to delete serial number char: %d", status);
}
status = aci_gatt_del_char(dev_info_svc->service_handle, dev_info_svc->firmware_rev_char_handle);
if(status) {
FURI_LOG_E(DEV_INFO_SVC_TAG, "Failed to delete firmware revision char: %d", status);
}
status = aci_gatt_del_char(dev_info_svc->service_handle, dev_info_svc->software_rev_char_handle);
if(status) {
FURI_LOG_E(DEV_INFO_SVC_TAG, "Failed to delete software revision char: %d", status);
}
// Delete service
status = aci_gatt_del_service(dev_info_svc->service_handle);
if(status) {
FURI_LOG_E(DEV_INFO_SVC_TAG, "Failed to delete device info service: %d", status);
}
free(dev_info_svc);
dev_info_svc = NULL;
}
}

View File

@ -0,0 +1,21 @@
#pragma once
#include <stdint.h>
#include <stdbool.h>
#ifdef __cplusplus
extern "C" {
#endif
#define DEV_INFO_MANUFACTURER_NAME "Flipper Devices Inc."
#define DEV_INFO_SERIAL_NUMBER "1.0"
#define DEV_INFO_FIRMWARE_REVISION_NUMBER TARGET
#define DEV_INFO_SOFTWARE_REVISION_NUMBER GIT_COMMIT " " GIT_BRANCH " " GIT_BRANCH_NUM " " BUILD_DATE
void dev_info_svc_start();
void dev_info_svc_stop();
#ifdef __cplusplus
}
#endif

View File

@ -1,157 +0,0 @@
#include "app_common.h"
#include "ble.h"
#include "dis_app.h"
#include <furi-hal-version.h>
#if ((BLE_CFG_DIS_SYSTEM_ID != 0) || (CFG_MENU_DEVICE_INFORMATION != 0))
static const uint8_t system_id[BLE_CFG_DIS_SYSTEM_ID_LEN_MAX] = {
(uint8_t)((DISAPP_MANUFACTURER_ID & 0xFF0000) >> 16),
(uint8_t)((DISAPP_MANUFACTURER_ID & 0x00FF00) >> 8),
(uint8_t)(DISAPP_MANUFACTURER_ID & 0x0000FF),
0xFE,
0xFF,
(uint8_t)((DISAPP_OUI & 0xFF0000) >> 16),
(uint8_t)((DISAPP_OUI & 0x00FF00) >> 8),
(uint8_t)(DISAPP_OUI & 0x0000FF)
};
#endif
#if ((BLE_CFG_DIS_IEEE_CERTIFICATION != 0) || (CFG_MENU_DEVICE_INFORMATION != 0))
static const uint8_t ieee_id[BLE_CFG_DIS_IEEE_CERTIFICATION_LEN_MAX] = {
0xFE, 0xCA, 0xFE, 0xCA, 0xFE, 0xCA, 0xFE, 0xCA,
0xFE, 0xCA, 0xFE, 0xCA, 0xFE, 0xCA, 0xFE, 0xCA,
0xFE, 0xCA, 0xFE, 0xCA, 0xFE, 0xCA, 0xFE, 0xCA,
0xFE, 0xCA, 0xFE, 0xCA, 0xFE, 0xCA, 0xFE, 0xCA,
};
#endif
#if ((BLE_CFG_DIS_PNP_ID != 0) || (CFG_MENU_DEVICE_INFORMATION != 0))
static const uint8_t pnp_id[BLE_CFG_DIS_PNP_ID_LEN_MAX] = {
0x1,
0xAD, 0xDE,
0xDE, 0xDA,
0x01, 0x00
};
#endif
void DISAPP_Init(void) {
DIS_Data_t dis_information_data;
#if ((BLE_CFG_DIS_MANUFACTURER_NAME_STRING != 0) || (CFG_MENU_DEVICE_INFORMATION != 0))
/**
* Update MANUFACTURER NAME Information
*
* @param UUID
* @param pPData
* @return
*/
dis_information_data.pPayload = (uint8_t*)DISAPP_MANUFACTURER_NAME;
dis_information_data.Length = sizeof(DISAPP_MANUFACTURER_NAME);
DIS_UpdateChar(MANUFACTURER_NAME_UUID, &dis_information_data);
#endif
#if ((BLE_CFG_DIS_MODEL_NUMBER_STRING != 0) || (CFG_MENU_DEVICE_INFORMATION != 0))
/**
* Update MODEL NUMBERInformation
*
* @param UUID
* @param pPData
* @return
*/
const char* name = furi_hal_version_get_device_name_ptr();
dis_information_data.pPayload = (uint8_t*)name;
dis_information_data.Length = strlen(name) + 1;
DIS_UpdateChar(MODEL_NUMBER_UUID, &dis_information_data);
#endif
#if ((BLE_CFG_DIS_SERIAL_NUMBER_STRING != 0) || (CFG_MENU_DEVICE_INFORMATION != 0))
/**
* Update SERIAL NUMBERInformation
*
* @param UUID
* @param pPData
* @return
*/
dis_information_data.pPayload = (uint8_t*)DISAPP_SERIAL_NUMBER;
dis_information_data.Length = sizeof(DISAPP_SERIAL_NUMBER);
DIS_UpdateChar(SERIAL_NUMBER_UUID, &dis_information_data);
#endif
#if ((BLE_CFG_DIS_HARDWARE_REVISION_STRING != 0) || (CFG_MENU_DEVICE_INFORMATION != 0))
/**
* Update HARDWARE REVISION NUMBERInformation
*
* @param UUID
* @param pPData
* @return
*/
dis_information_data.pPayload = (uint8_t*)DISAPP_HARDWARE_REVISION_NUMBER;
dis_information_data.Length = sizeof(DISAPP_HARDWARE_REVISION_NUMBER);
DIS_UpdateChar(HARDWARE_REVISION_UUID, &dis_information_data);
#endif
#if ((BLE_CFG_DIS_FIRMWARE_REVISION_STRING != 0) || (CFG_MENU_DEVICE_INFORMATION != 0))
/**
* Update FIRMWARE REVISION NUMBERInformation
*
* @param UUID
* @param pPData
* @return
*/
dis_information_data.pPayload = (uint8_t*)DISAPP_FIRMWARE_REVISION_NUMBER;
dis_information_data.Length = sizeof(DISAPP_FIRMWARE_REVISION_NUMBER);
DIS_UpdateChar(FIRMWARE_REVISION_UUID, &dis_information_data);
#endif
#if ((BLE_CFG_DIS_SOFTWARE_REVISION_STRING != 0) || (CFG_MENU_DEVICE_INFORMATION != 0))
/**
* Update SOFTWARE REVISION NUMBERInformation
*
* @param UUID
* @param pPData
* @return
*/
dis_information_data.pPayload = (uint8_t*)DISAPP_SOFTWARE_REVISION_NUMBER;
dis_information_data.Length = sizeof(DISAPP_SOFTWARE_REVISION_NUMBER);
DIS_UpdateChar(SOFTWARE_REVISION_UUID, &dis_information_data);
#endif
#if ((BLE_CFG_DIS_SYSTEM_ID != 0) || (CFG_MENU_DEVICE_INFORMATION != 0))
/**
* Update SYSTEM ID Information
*
* @param UUID
* @param pPData
* @return
*/
dis_information_data.pPayload = (uint8_t *)system_id;
dis_information_data.Length = BLE_CFG_DIS_SYSTEM_ID_LEN_MAX;
DIS_UpdateChar(SYSTEM_ID_UUID, &dis_information_data);
#endif
#if ((BLE_CFG_DIS_IEEE_CERTIFICATION != 0) || (CFG_MENU_DEVICE_INFORMATION != 0))
/**
* Update IEEE CERTIFICATION ID Information
*
* @param UUID
* @param pPData
* @return
*/
dis_information_data.pPayload = (uint8_t *)ieee_id;
dis_information_data.Length = BLE_CFG_DIS_IEEE_CERTIFICATION_LEN_MAX;
DIS_UpdateChar(IEEE_CERTIFICATION_UUID, &dis_information_data);
#endif
#if ((BLE_CFG_DIS_PNP_ID != 0) || (CFG_MENU_DEVICE_INFORMATION != 0))
/**
* Update PNP ID Information
*
* @param UUID
* @param pPData
* @return
*/
dis_information_data.pPayload = (uint8_t *)pnp_id;
dis_information_data.Length = BLE_CFG_DIS_PNP_ID_LEN_MAX;
DIS_UpdateChar(PNP_ID_UUID, &dis_information_data);
#endif
}

View File

@ -1,20 +0,0 @@
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
#define DISAPP_MANUFACTURER_NAME "Flipperdevice Inc."
//#define DISAPP_MODEL_NUMBER "FlipperZero"
#define DISAPP_SERIAL_NUMBER "1.0"
#define DISAPP_HARDWARE_REVISION_NUMBER "1.0"
#define DISAPP_FIRMWARE_REVISION_NUMBER TARGET
#define DISAPP_SOFTWARE_REVISION_NUMBER GIT_COMMIT " " GIT_BRANCH " " GIT_BRANCH_NUM " " BUILD_DATE
#define DISAPP_OUI 0x123456
#define DISAPP_MANUFACTURER_ID 0x9ABCDE
void DISAPP_Init(void);
#ifdef __cplusplus
}
#endif

View File

@ -0,0 +1,387 @@
#include "gap.h"
#include "app_entry.h"
#include "ble.h"
#include "cmsis_os.h"
#include "otp.h"
#include "dev_info_service.h"
#include "battery_service.h"
#include "serial_service.h"
#include <applications/bt/bt_service/bt.h>
#include <furi-hal.h>
#define GAP_TAG "BLE"
#define FAST_ADV_TIMEOUT 30000
#define INITIAL_ADV_TIMEOUT 60000
#define BD_ADDR_SIZE_LOCAL 6
typedef struct {
uint16_t gap_svc_handle;
uint16_t dev_name_char_handle;
uint16_t appearance_char_handle;
uint16_t connection_handle;
uint8_t adv_svc_uuid_len;
uint8_t adv_svc_uuid[20];
} GapSvc;
typedef struct {
GapSvc gap_svc;
GapState state;
uint8_t mac_address[BD_ADDR_SIZE_LOCAL];
Bt* bt;
osTimerId advertise_timer;
osThreadAttr_t thread_attr;
osThreadId_t thread_id;
} Gap;
// Identity root key
static const uint8_t gap_irk[16] = {0x12,0x34,0x56,0x78,0x9a,0xbc,0xde,0xf0,0x12,0x34,0x56,0x78,0x9a,0xbc,0xde,0xf0};
// Encryption root key
static const uint8_t gap_erk[16] = {0xfe,0xdc,0xba,0x09,0x87,0x65,0x43,0x21,0xfe,0xdc,0xba,0x09,0x87,0x65,0x43,0x21};
// Appearence characteristic UUID
static const uint8_t gap_appearence_char_uuid[] = {0x00, 0x86};
// Default MAC address
static const uint8_t gap_default_mac_addr[] = {0x6c, 0x7a, 0xd8, 0xac, 0x57, 0x72};
static Gap* gap = NULL;
static void gap_advertise(GapState new_state);
static void gap_app(void *arg);
SVCCTL_UserEvtFlowStatus_t SVCCTL_App_Notification( void *pckt )
{
hci_event_pckt *event_pckt;
evt_le_meta_event *meta_evt;
evt_blue_aci *blue_evt;
hci_le_phy_update_complete_event_rp0 *evt_le_phy_update_complete;
uint8_t tx_phy;
uint8_t rx_phy;
tBleStatus ret = BLE_STATUS_INVALID_PARAMS;
event_pckt = (hci_event_pckt*) ((hci_uart_pckt *) pckt)->data;
switch (event_pckt->evt) {
case EVT_DISCONN_COMPLETE:
{
hci_disconnection_complete_event_rp0 *disconnection_complete_event = (hci_disconnection_complete_event_rp0 *) event_pckt->data;
if (disconnection_complete_event->Connection_Handle == gap->gap_svc.connection_handle) {
gap->gap_svc.connection_handle = 0;
gap->state = GapStateIdle;
FURI_LOG_I(GAP_TAG, "Disconnect from client");
}
// Restart advertising
gap_advertise(GapStateAdvFast);
furi_hal_power_insomnia_exit();
}
break;
case EVT_LE_META_EVENT:
meta_evt = (evt_le_meta_event*) event_pckt->data;
switch (meta_evt->subevent) {
case EVT_LE_CONN_UPDATE_COMPLETE:
FURI_LOG_D(GAP_TAG, "Connection update event");
break;
case EVT_LE_PHY_UPDATE_COMPLETE:
evt_le_phy_update_complete = (hci_le_phy_update_complete_event_rp0*)meta_evt->data;
if(evt_le_phy_update_complete->Status) {
FURI_LOG_E(GAP_TAG, "Update PHY failed, status %d", evt_le_phy_update_complete->Status);
} else {
FURI_LOG_I(GAP_TAG, "Update PHY succeed");
}
ret = hci_le_read_phy(gap->gap_svc.connection_handle,&tx_phy,&rx_phy);
if(ret) {
FURI_LOG_E(GAP_TAG, "Read PHY failed, status: %d", ret);
} else {
FURI_LOG_I(GAP_TAG, "PHY Params TX= %d, RX= %d ", tx_phy, rx_phy);
}
break;
case EVT_LE_CONN_COMPLETE:
furi_hal_power_insomnia_enter();
hci_le_connection_complete_event_rp0* connection_complete_event = (hci_le_connection_complete_event_rp0 *) meta_evt->data;
FURI_LOG_I(GAP_TAG, "Connection complete for connection handle 0x%x", connection_complete_event->Connection_Handle);
// Stop advertising as connection completed
osTimerStop(gap->advertise_timer);
// Update connection status and handle
gap->state = GapStateConnected;
gap->gap_svc.connection_handle = connection_complete_event->Connection_Handle;
// Start pairing by sending security request
aci_gap_slave_security_req(connection_complete_event->Connection_Handle);
break;
default:
break;
}
break;
case EVT_VENDOR:
blue_evt = (evt_blue_aci*) event_pckt->data;
switch (blue_evt->ecode) {
aci_gap_pairing_complete_event_rp0 *pairing_complete;
case EVT_BLUE_GAP_LIMITED_DISCOVERABLE:
FURI_LOG_I(GAP_TAG, "Limited discoverable event");
break;
case EVT_BLUE_GAP_PASS_KEY_REQUEST:
{
// Generate random PIN code
uint32_t pin = rand() % 999999;
aci_gap_pass_key_resp(gap->gap_svc.connection_handle, pin);
FURI_LOG_I(GAP_TAG, "Pass key request event. Pin: %d", pin);
bt_pin_code_show(gap->bt, pin);
}
break;
case EVT_BLUE_GAP_AUTHORIZATION_REQUEST:
FURI_LOG_I(GAP_TAG, "Authorization request event");
break;
case EVT_BLUE_GAP_SLAVE_SECURITY_INITIATED:
FURI_LOG_I(GAP_TAG, "Slave security initiated");
break;
case EVT_BLUE_GAP_BOND_LOST:
FURI_LOG_I(GAP_TAG, "Bond lost event. Start rebonding");
aci_gap_allow_rebond(gap->gap_svc.connection_handle);
break;
case EVT_BLUE_GAP_DEVICE_FOUND:
FURI_LOG_I(GAP_TAG, "Device found event");
break;
case EVT_BLUE_GAP_ADDR_NOT_RESOLVED:
FURI_LOG_I(GAP_TAG, "Address not resolved event");
break;
case EVT_BLUE_GAP_KEYPRESS_NOTIFICATION:
FURI_LOG_I(GAP_TAG, "Key press notification event");
break;
case EVT_BLUE_GAP_NUMERIC_COMPARISON_VALUE:
FURI_LOG_I(GAP_TAG, "Hex_value = %lx",
((aci_gap_numeric_comparison_value_event_rp0 *)(blue_evt->data))->Numeric_Value);
aci_gap_numeric_comparison_value_confirm_yesno(gap->gap_svc.connection_handle, 1);
break;
case (EVT_BLUE_GAP_PAIRING_CMPLT):
{
pairing_complete = (aci_gap_pairing_complete_event_rp0*)blue_evt->data;
if (pairing_complete->Status) {
FURI_LOG_E(GAP_TAG, "Pairing failed with status: %d. Terminating connection", pairing_complete->Status);
aci_gap_terminate(gap->gap_svc.connection_handle, 5);
} else {
FURI_LOG_I(GAP_TAG, "Pairing complete");
}
}
break;
case EVT_BLUE_GAP_PROCEDURE_COMPLETE:
FURI_LOG_I(GAP_TAG, "Procedure complete event");
break;
}
default:
break;
}
return SVCCTL_UserEvtFlowEnable;
}
void SVCCTL_SvcInit() {
// Dummy function to prevent unused services initialization
// TODO refactor (disable all services in WPAN config)
}
static void set_advertisment_service_uid(uint8_t* uid, uint8_t uid_len) {
gap->gap_svc.adv_svc_uuid_len = 1;
if(uid_len == 2) {
gap->gap_svc.adv_svc_uuid[0] = AD_TYPE_16_BIT_SERV_UUID;
} else if (uid_len == 4) {
gap->gap_svc.adv_svc_uuid[0] = AD_TYPE_32_BIT_SERV_UUID;
} else if(uid_len == 16) {
gap->gap_svc.adv_svc_uuid[0] = AD_TYPE_128_BIT_SERV_UUID_CMPLT_LIST;
}
memcpy(&gap->gap_svc.adv_svc_uuid[1], uid, uid_len);
gap->gap_svc.adv_svc_uuid_len += uid_len;
}
GapState gap_get_status() {
return gap->state;
}
void gap_init_mac_address(Gap* gap) {
uint8_t *otp_addr;
uint32_t udn;
uint32_t company_id;
uint32_t device_id;
udn = LL_FLASH_GetUDN();
if(udn != 0xFFFFFFFF) {
company_id = LL_FLASH_GetSTCompanyID();
device_id = LL_FLASH_GetDeviceID();
gap->mac_address[0] = (uint8_t)(udn & 0x000000FF);
gap->mac_address[1] = (uint8_t)( (udn & 0x0000FF00) >> 8 );
gap->mac_address[2] = (uint8_t)( (udn & 0x00FF0000) >> 16 );
gap->mac_address[3] = (uint8_t)device_id;
gap->mac_address[4] = (uint8_t)(company_id & 0x000000FF);;
gap->mac_address[5] = (uint8_t)( (company_id & 0x0000FF00) >> 8 );
} else {
otp_addr = OTP_Read(0);
if(otp_addr) {
memcpy(gap->mac_address, ((OTP_ID0_t*)otp_addr)->bd_address, sizeof(gap->mac_address));
} else {
memcpy(gap->mac_address, gap_default_mac_addr, sizeof(gap->mac_address));
}
}
}
static void gap_init_svc(Gap* gap) {
tBleStatus status;
uint32_t srd_bd_addr[2];
//HCI Reset to synchronise BLE Stack*/
hci_reset();
// Configure mac address
gap_init_mac_address(gap);
aci_hal_write_config_data(CONFIG_DATA_PUBADDR_OFFSET, CONFIG_DATA_PUBADDR_LEN, (uint8_t*)gap->mac_address);
/* Static random Address
* The two upper bits shall be set to 1
* The lowest 32bits is read from the UDN to differentiate between devices
* The RNG may be used to provide a random number on each power on
*/
srd_bd_addr[1] = 0x0000ED6E;
srd_bd_addr[0] = LL_FLASH_GetUDN();
aci_hal_write_config_data( CONFIG_DATA_RANDOM_ADDRESS_OFFSET, CONFIG_DATA_RANDOM_ADDRESS_LEN, (uint8_t*)srd_bd_addr );
// Set Identity root key used to derive LTK and CSRK
aci_hal_write_config_data( CONFIG_DATA_IR_OFFSET, CONFIG_DATA_IR_LEN, (uint8_t*)gap_irk );
// Set Encryption root key used to derive LTK and CSRK
aci_hal_write_config_data( CONFIG_DATA_ER_OFFSET, CONFIG_DATA_ER_LEN, (uint8_t*)gap_erk );
// Set TX Power to 0 dBm
aci_hal_set_tx_power_level(1, 0x19);
// Initialize GATT interface
aci_gatt_init();
// Initialize GAP interface
const char *name = furi_hal_version_get_device_name_ptr();
aci_gap_init(GAP_PERIPHERAL_ROLE, 0, strlen(name),
&gap->gap_svc.gap_svc_handle, &gap->gap_svc.dev_name_char_handle, &gap->gap_svc.appearance_char_handle);
// Set GAP characteristics
status = aci_gatt_update_char_value(gap->gap_svc.gap_svc_handle, gap->gap_svc.dev_name_char_handle, 0, strlen(name), (uint8_t *) name);
if (status) {
FURI_LOG_E(GAP_TAG, "Failed updating name characteristic: %d", status);
}
status = aci_gatt_update_char_value(gap->gap_svc.gap_svc_handle, gap->gap_svc.appearance_char_handle, 0, 2, gap_appearence_char_uuid);
if(status) {
FURI_LOG_E(GAP_TAG, "Failed updating appearence characteristic: %d", status);
}
// Set default PHY
hci_le_set_default_phy(ALL_PHYS_PREFERENCE, TX_2M_PREFERRED, RX_2M_PREFERRED);
// Set I/O capability
aci_gap_set_io_capability(IO_CAP_DISPLAY_ONLY);
// Setup authentication
aci_gap_set_authentication_requirement(1, 1, 1, 0, 8, 16, 1, 0, PUBLIC_ADDR);
// Configure whitelist
aci_gap_configure_whitelist();
}
static void gap_advertise(GapState new_state)
{
tBleStatus status;
uint16_t min_interval;
uint16_t max_interval;
if (new_state == GapStateAdvFast) {
min_interval = 0x80; // 80 ms
max_interval = 0xa0; // 100 ms
} else {
min_interval = 0x0640; // 1 s
max_interval = 0x0fa0; // 2.5 s
}
// Stop advertising timer
osTimerStop(gap->advertise_timer);
if ((new_state == GapStateAdvLowPower) && ((gap->state == GapStateAdvFast) || (gap->state == GapStateAdvLowPower))) {
// Stop advertising
status = aci_gap_set_non_discoverable();
if (status) {
FURI_LOG_E(GAP_TAG, "Stop Advertising Failed, result: %d", status);
}
}
// Configure advertising
gap->state = new_state;
const char* name = furi_hal_version_get_ble_local_device_name_ptr();
status = aci_gap_set_discoverable(ADV_IND, min_interval, max_interval, PUBLIC_ADDR, 0,
strlen(name), (uint8_t*)name,
gap->gap_svc.adv_svc_uuid_len, gap->gap_svc.adv_svc_uuid, 0, 0);
if(status) {
FURI_LOG_E(GAP_TAG, "Set discoverable err: %d", status);
}
osTimerStart(gap->advertise_timer, INITIAL_ADV_TIMEOUT);
}
static void gap_advertise_request(Gap* gap) {
osThreadFlagsSet(gap->thread_id, 1);
}
static void gap_advetise_timer_callback(void* context) {
furi_assert(context);
Gap* gap = context;
gap_advertise_request(gap);
}
bool gap_init() {
if (APPE_Status() != BleGlueStatusStarted) {
return false;
}
gap = furi_alloc(sizeof(Gap));
srand(DWT->CYCCNT);
// Open Bt record
gap->bt = furi_record_open("bt");
// Create advertising timer
gap->advertise_timer = osTimerNew(gap_advetise_timer_callback, osTimerOnce, &gap, NULL);
// Initialization of HCI & GATT & GAP layer
gap_init_svc(gap);
// Initialization of the BLE Services
SVCCTL_Init();
// Initialization of the BLE App Context
gap->state = GapStateIdle;
gap->gap_svc.connection_handle = 0xFFFF;
// Thread configuration
gap->thread_attr.name = "BLE advertising";
gap->thread_attr.stack_size = 512;
gap->thread_id = osThreadNew(gap_app, NULL, &gap->thread_attr);
// Start Device Information service
dev_info_svc_start();
// Start Battery service
battery_svc_start();
// Start Serial application
serial_svc_start();
// Configure advirtise service UUID
uint8_t adv_service_uid[2];
adv_service_uid[0] = 0x80 | furi_hal_version_get_hw_color();
adv_service_uid[1] = 0x30;
set_advertisment_service_uid(adv_service_uid, sizeof(adv_service_uid));
gap_advertise(GapStateAdvFast);
return true;
}
static void gap_app(void *arg) {
// TODO Exit from app, stop service, clean memory
while(1) {
osThreadFlagsWait(1, osFlagsWaitAny, osWaitForever);
gap_advertise(GapStateAdvLowPower);
}
}

View File

@ -0,0 +1,22 @@
#pragma once
#include <stdbool.h>
#ifdef __cplusplus
extern "C" {
#endif
typedef enum {
GapStateIdle,
GapStateAdvFast,
GapStateAdvLowPower,
GapStateConnected,
} GapState;
bool gap_init();
GapState gap_get_status();
#ifdef __cplusplus
}
#endif

View File

@ -1,256 +0,0 @@
/* USER CODE BEGIN Header */
/**
******************************************************************************
* @file hrs_app.c
* @author MCD Application Team
* @brief Heart Rate Service Application
******************************************************************************
* @attention
*
* <h2><center>&copy; Copyright (c) 2019 STMicroelectronics.
* All rights reserved.</center></h2>
*
* This software component is licensed by ST under Ultimate Liberty license
* SLA0044, the "License"; You may not use this file except in compliance with
* the License. You may obtain a copy of the License at:
* www.st.com/SLA0044
*
******************************************************************************
*/
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "app_common.h"
#include "ble.h"
#include "hrs_app.h"
#include "cmsis_os.h"
/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
/* USER CODE END Includes */
/* Private typedef -----------------------------------------------------------*/
typedef struct
{
HRS_BodySensorLocation_t BodySensorLocationChar;
HRS_MeasVal_t MeasurementvalueChar;
uint8_t ResetEnergyExpended;
uint8_t TimerMeasurement_Id;
} HRSAPP_Context_t;
/* USER CODE BEGIN PTD */
/* USER CODE END PTD */
/* Private defines ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
/* USER CODE END PD */
/* Private macros ------------------------------------------------------------*/
#define HRSAPP_MEASUREMENT_INTERVAL (1000000/CFG_TS_TICK_VAL) /**< 1s */
/* USER CODE BEGIN PM */
/* USER CODE END PM */
/* Private variables ---------------------------------------------------------*/
/**
* START of Section BLE_APP_CONTEXT
*/
PLACE_IN_SECTION("BLE_APP_CONTEXT") static HRSAPP_Context_t HRSAPP_Context;
/**
* END of Section BLE_APP_CONTEXT
*/
osThreadId_t HrsProcessId;
const osThreadAttr_t HrsProcess_attr = {
.name = CFG_HRS_PROCESS_NAME,
.attr_bits = CFG_HRS_PROCESS_ATTR_BITS,
.cb_mem = CFG_HRS_PROCESS_CB_MEM,
.cb_size = CFG_HRS_PROCESS_CB_SIZE,
.stack_mem = CFG_HRS_PROCESS_STACK_MEM,
.priority = CFG_HRS_PROCESS_PRIORITY,
.stack_size = CFG_HRS_PROCESS_STACK_SIZE
};
/* USER CODE BEGIN PV */
/* USER CODE END PV */
/* Private functions prototypes-----------------------------------------------*/
static void HrMeas( void );
static void HrsProcess(void *argument);
static void HRSAPP_Measurement(void);
static uint32_t HRSAPP_Read_RTC_SSR_SS ( void );
/* USER CODE BEGIN PFP */
/* USER CODE END PFP */
/* Functions Definition ------------------------------------------------------*/
void HRS_Notification(HRS_App_Notification_evt_t *pNotification)
{
/* USER CODE BEGIN HRS_Notification_1 */
/* USER CODE END HRS_Notification_1 */
switch(pNotification->HRS_Evt_Opcode)
{
/* USER CODE BEGIN HRS_Notification_HRS_Evt_Opcode */
/* USER CODE END HRS_Notification_HRS_Evt_Opcode */
#if (BLE_CFG_HRS_ENERGY_EXPENDED_INFO_FLAG != 0)
case HRS_RESET_ENERGY_EXPENDED_EVT:
/* USER CODE BEGIN HRS_RESET_ENERGY_EXPENDED_EVT */
HRSAPP_Context.MeasurementvalueChar.EnergyExpended = 0;
HRSAPP_Context.ResetEnergyExpended = 1;
/* USER CODE END HRS_RESET_ENERGY_EXPENDED_EVT */
break;
#endif
case HRS_NOTIFICATION_ENABLED:
/* USER CODE BEGIN HRS_NOTIFICATION_ENABLED */
/**
* It could be the enable notification is received twice without the disable notification in between
*/
HW_TS_Stop(HRSAPP_Context.TimerMeasurement_Id);
HW_TS_Start(HRSAPP_Context.TimerMeasurement_Id, HRSAPP_MEASUREMENT_INTERVAL);
/* USER CODE END HRS_NOTIFICATION_ENABLED */
break;
case HRS_NOTIFICATION_DISABLED:
/* USER CODE BEGIN HRS_NOTIFICATION_DISABLED */
HW_TS_Stop(HRSAPP_Context.TimerMeasurement_Id);
/* USER CODE END HRS_NOTIFICATION_DISABLED */
break;
#if (BLE_CFG_OTA_REBOOT_CHAR != 0)
case HRS_STM_BOOT_REQUEST_EVT:
/* USER CODE BEGIN HRS_STM_BOOT_REQUEST_EVT */
*(uint32_t*)SRAM1_BASE = *(uint32_t*)pNotification->DataTransfered.pPayload;
NVIC_SystemReset();
/* USER CODE END HRS_STM_BOOT_REQUEST_EVT */
break;
#endif
default:
/* USER CODE BEGIN HRS_Notification_Default */
/* USER CODE END HRS_Notification_Default */
break;
}
/* USER CODE BEGIN HRS_Notification_2 */
/* USER CODE END HRS_Notification_2 */
return;
}
void HRSAPP_Init(void)
{
HrsProcessId = osThreadNew(HrsProcess, NULL, &HrsProcess_attr);
/* USER CODE BEGIN HRSAPP_Init */
/**
* Set Body Sensor Location
*/
HRSAPP_Context.ResetEnergyExpended = 0;
HRSAPP_Context.BodySensorLocationChar = HRS_BODY_SENSOR_LOCATION_HAND;
HRS_UpdateChar(SENSOR_LOCATION_UUID, (uint8_t *)&HRSAPP_Context.BodySensorLocationChar);
/**
* Set Flags for measurement value
*/
HRSAPP_Context.MeasurementvalueChar.Flags = ( HRS_HRM_VALUE_FORMAT_UINT16 |
HRS_HRM_SENSOR_CONTACTS_PRESENT |
HRS_HRM_SENSOR_CONTACTS_SUPPORTED |
HRS_HRM_ENERGY_EXPENDED_PRESENT |
HRS_HRM_RR_INTERVAL_PRESENT );
#if (BLE_CFG_HRS_ENERGY_EXPENDED_INFO_FLAG != 0)
if(HRSAPP_Context.MeasurementvalueChar.Flags & HRS_HRM_ENERGY_EXPENDED_PRESENT)
HRSAPP_Context.MeasurementvalueChar.EnergyExpended = 10;
#endif
#if (BLE_CFG_HRS_ENERGY_RR_INTERVAL_FLAG != 0)
if(HRSAPP_Context.MeasurementvalueChar.Flags & HRS_HRM_RR_INTERVAL_PRESENT)
{
uint8_t i;
HRSAPP_Context.MeasurementvalueChar.NbreOfValidRRIntervalValues = BLE_CFG_HRS_ENERGY_RR_INTERVAL_FLAG;
for(i = 0; i < BLE_CFG_HRS_ENERGY_RR_INTERVAL_FLAG; i++)
HRSAPP_Context.MeasurementvalueChar.aRRIntervalValues[i] = 1024;
}
#endif
/**
* Create timer for Heart Rate Measurement
*/
HW_TS_Create(CFG_TIM_PROC_ID_ISR, &(HRSAPP_Context.TimerMeasurement_Id), hw_ts_Repeated, HrMeas);
/* USER CODE END HRSAPP_Init */
return;
}
static void HrsProcess(void *argument)
{
UNUSED(argument);
for(;;)
{
osThreadFlagsWait( 1, osFlagsWaitAny, osWaitForever);
HRSAPP_Measurement( );
}
}
static void HRSAPP_Measurement(void)
{
/* USER CODE BEGIN HRSAPP_Measurement */
uint32_t measurement;
measurement = ((HRSAPP_Read_RTC_SSR_SS()) & 0x07) + 65;
HRSAPP_Context.MeasurementvalueChar.MeasurementValue = measurement;
#if (BLE_CFG_HRS_ENERGY_EXPENDED_INFO_FLAG != 0)
if((HRSAPP_Context.MeasurementvalueChar.Flags & HRS_HRM_ENERGY_EXPENDED_PRESENT) &&
(HRSAPP_Context.ResetEnergyExpended == 0))
HRSAPP_Context.MeasurementvalueChar.EnergyExpended += 5;
else if(HRSAPP_Context.ResetEnergyExpended == 1)
HRSAPP_Context.ResetEnergyExpended = 0;
#endif
HRS_UpdateChar(HEART_RATE_MEASURMENT_UUID, (uint8_t *)&HRSAPP_Context.MeasurementvalueChar);
/* USER CODE END HRSAPP_Measurement */
return;
}
static void HrMeas( void )
{
/**
* The code shall be executed in the background as aci command may be sent
* The background is the only place where the application can make sure a new aci command
* is not sent if there is a pending one
*/
osThreadFlagsSet( HrsProcessId, 1 );
/* USER CODE BEGIN HrMeas */
/* USER CODE END HrMeas */
return;
}
static uint32_t HRSAPP_Read_RTC_SSR_SS ( void )
{
return ((uint32_t)(READ_BIT(RTC->SSR, RTC_SSR_SS)));
}
/* USER CODE BEGIN FD */
/* USER CODE END FD */
/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/

View File

@ -0,0 +1,122 @@
#include "serial_service.h"
#include "app_common.h"
#include "ble.h"
#include <furi.h>
#define SERIAL_SERVICE_TAG "serial service"
#define SERIAL_SVC_DATA_LEN_MAX 245
typedef struct {
uint16_t svc_handle;
uint16_t rx_char_handle;
uint16_t tx_char_handle;
} SerialSvc;
static SerialSvc* serial_svc;
static const uint8_t service_uuid[] = {0x00, 0x00, 0xfe, 0x60, 0xcc, 0x7a, 0x48, 0x2a, 0x98, 0x4a, 0x7f, 0x2e, 0xd5, 0xb3, 0xe5, 0x8f};
static const uint8_t char_rx_uuid[] = {0x00, 0x00, 0xfe, 0x61, 0x8e, 0x22, 0x45, 0x41, 0x9d, 0x4c, 0x21, 0xed, 0xae, 0x82, 0xed, 0x19};
static const uint8_t char_tx_uuid[] = {0x00, 0x00, 0xfe, 0x62, 0x8e, 0x22, 0x45, 0x41, 0x9d, 0x4c, 0x21, 0xed, 0xae, 0x82, 0xed, 0x19};
static SVCCTL_EvtAckStatus_t serial_svc_event_handler(void *event) {
SVCCTL_EvtAckStatus_t ret = SVCCTL_EvtNotAck;
hci_event_pckt* event_pckt = (hci_event_pckt *)(((hci_uart_pckt*)event)->data);
evt_blecore_aci* blecore_evt = (evt_blecore_aci*)event_pckt->data;
aci_gatt_attribute_modified_event_rp0* attribute_modified;
if(event_pckt->evt == HCI_VENDOR_SPECIFIC_DEBUG_EVT_CODE) {
if(blecore_evt->ecode == ACI_GATT_ATTRIBUTE_MODIFIED_VSEVT_CODE) {
attribute_modified = (aci_gatt_attribute_modified_event_rp0*)blecore_evt->data;
if(attribute_modified->Attr_Handle == serial_svc->tx_char_handle + 2) {
// Descriptor handle
ret = SVCCTL_EvtAckFlowEnable;
FURI_LOG_D(SERIAL_SERVICE_TAG, "TX descriptor event");
} else if(attribute_modified->Attr_Handle == serial_svc->tx_char_handle + 1) {
FURI_LOG_D(SERIAL_SERVICE_TAG, "Received %d bytes", attribute_modified->Attr_Data_Length);
serial_svc_update_rx(attribute_modified->Attr_Data, attribute_modified->Attr_Data_Length);
ret = SVCCTL_EvtAckFlowEnable;
}
} else if(blecore_evt->ecode == ACI_GATT_SERVER_CONFIRMATION_VSEVT_CODE) {
FURI_LOG_D(SERIAL_SERVICE_TAG, "Ack received", blecore_evt->ecode);
ret = SVCCTL_EvtAckFlowEnable;
}
}
return ret;
}
void serial_svc_start() {
tBleStatus status;
serial_svc = furi_alloc(sizeof(SerialSvc));
// Register event handler
SVCCTL_RegisterSvcHandler(serial_svc_event_handler);
// Add service
status = aci_gatt_add_service(UUID_TYPE_128, (Service_UUID_t *)service_uuid, PRIMARY_SERVICE, 6, &serial_svc->svc_handle);
if(status) {
FURI_LOG_E(SERIAL_SERVICE_TAG, "Failed to add Serial service: %d", status);
}
// Add TX characteristics
status = aci_gatt_add_char(serial_svc->svc_handle, UUID_TYPE_128, (const Char_UUID_t*)char_tx_uuid,
SERIAL_SVC_DATA_LEN_MAX,
CHAR_PROP_WRITE_WITHOUT_RESP | CHAR_PROP_WRITE | CHAR_PROP_READ,
ATTR_PERMISSION_NONE,
GATT_NOTIFY_ATTRIBUTE_WRITE,
10,
CHAR_VALUE_LEN_VARIABLE,
&serial_svc->tx_char_handle);
if(status) {
FURI_LOG_E(SERIAL_SERVICE_TAG, "Failed to add TX characteristic: %d", status);
}
// Add RX characteristic
status = aci_gatt_add_char(serial_svc->svc_handle, UUID_TYPE_128, (const Char_UUID_t*)char_rx_uuid,
SERIAL_SVC_DATA_LEN_MAX,
CHAR_PROP_READ | CHAR_PROP_INDICATE,
ATTR_PERMISSION_NONE,
GATT_DONT_NOTIFY_EVENTS,
10,
CHAR_VALUE_LEN_VARIABLE,
&serial_svc->rx_char_handle);
if(status) {
FURI_LOG_E(SERIAL_SERVICE_TAG, "Failed to add RX characteristic: %d", status);
}
}
void serial_svc_stop() {
tBleStatus status;
if(serial_svc) {
// Delete characteristics
status = aci_gatt_del_char(serial_svc->svc_handle, serial_svc->tx_char_handle);
if(status) {
FURI_LOG_E(SERIAL_SERVICE_TAG, "Failed to delete TX characteristic: %d", status);
}
status = aci_gatt_del_char(serial_svc->svc_handle, serial_svc->rx_char_handle);
if(status) {
FURI_LOG_E(SERIAL_SERVICE_TAG, "Failed to delete RX characteristic: %d", status);
}
// Delete service
status = aci_gatt_del_service(serial_svc->svc_handle);
if(status) {
FURI_LOG_E(SERIAL_SERVICE_TAG, "Failed to delete Serial service: %d", status);
}
free(serial_svc);
serial_svc = NULL;
}
}
bool serial_svc_update_rx(uint8_t* data, uint8_t data_len) {
furi_assert(data_len < SERIAL_SVC_DATA_LEN_MAX);
tBleStatus result = aci_gatt_update_char_value(serial_svc->svc_handle,
serial_svc->rx_char_handle,
0,
data_len,
data);
if(result) {
FURI_LOG_E(SERIAL_SERVICE_TAG, "Failed updating RX characteristic: %d", result);
}
return result != BLE_STATUS_SUCCESS;
}

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